Nhịp thư (Email Orchestrator): PRD
Domain Communications · function
orchestrator· mã storyCOMM-ORCH· SDD · Kế hoạch · Tests · vận hành: platform/emailsHiện trạng: đã viết mã, ship ở chế độ
preview(14.10.2026). Hệ lập kế hoạch và gửi bản xem trước cho hai hộp thư duyệt mỗi ngày, không gửi cho người dùng nào cho tới khi người soạn tự chuyển sangliveở admin. Mọi mặc định là mặc định an toàn (mục 5); bậtlivecần qua danh sách kiểm ở mục 14.
1. Vấn đề
Người soạn muốn mỗi người đã đăng ký Conan School nhận thư định kỳ (sự kiện sắp tới, buổi học sắp tới, tuần qua có gì mới...), không bị coi là spam, không quá ít, tối đa một thư mỗi ngày, và mỗi người nhận đúng thư mà khả năng cao họ sẽ mở và thấy có ý nghĩa. Mọi thư phải tới hộp thư người soạn ([email protected], [email protected]) trước 24 giờ để duyệt và chỉnh.
Hệ cũ có daily.ts: một chuỗi if cố định bốn loại, chỉ cho bảng users, và mỗi ngày gửi 0 đến 11 thư vì gần như không ai khớp luật nào (đo 03-13.10.2026).
2. Số liệu thật (đo trên production 14.10.2026)
Đây là nền của mọi quyết định bên dưới; "hàng nghìn người" thực tế là:
| Đại lượng | Giá trị |
|---|---|
Địa chỉ khác nhau (users ∪ legacy_users, khử trùng theo lower(trim(email))) | 1.264 |
Trong đó có tài khoản ở hệ mới (users) | 154 |
Chỉ có ở hệ cũ (legacy_users), đăng nhập Google, ngủ từ tháng 4/2026 | 1.110 |
Người hệ cũ đang hoạt động (đếm lại khi rà soát, bỏ seed_forum), theo thư thời Resend | 1.090: từng bấm 66 · từng mở 467 · nhận mà chưa mở 482 · chưa từng nhận 75. Lượt gửi cuối 25.04.2026 |
| Hộp thư Gmail | 98,2% |
| Người hệ cũ từng đăng ký workshop AI/viết nội dung | 646 (tín hiệu sở thích duy nhất của nhóm này) |
| Tài khoản vào web trong 14 ngày qua | 39 · tài khoản tạo trong 14 ngày: 13 |
| Tài khoản chưa có cộng đồng | 101, khoảng 65% tài khoản (92 người đã nhận đủ 3 thư profile_incomplete) |
| Đỉnh gửi một ngày của miền tới nay | 141 thư (08.09.2026) |
| Lượt mở có nhiễu | 81% qua proxy Gmail; ~11% người nhận có lượt mở máy (Apple MPP, máy quét Gmail) |
| Tỉ lệ tương tác đo được của thư gắn kết, 90 ngày | ~0,13 toàn hệ: profile_incomplete 0,10 (n=343), learning_nudge 0,19 (n=15), community_weekly 0,27 (n=40) |
| Theo dõi lượt bấm | không có trước Nhịp thư |
| Nội dung mới mỗi tuần | rất ít: 4 buổi Bàn tròn sắp tới (mỗi buổi của một cộng đồng), buổi "AI for Biz" hằng tuần tạo ngay trong ngày, What's New chỉ có tuần W41, 0 khoá mới có thành viên |
| Nội dung "thường xanh" | 606 câu chuyện unit (SCQA) thuộc cộng đồng đang mở, 232 sách của trường Conan, 90 Job JTBD miễn phí, 70 playbook |
Hệ quả: nhịp một thư mỗi ngày cho mọi người không nuôi được bằng tin mới. Thư phải dựa vào việc riêng của từng người (khoá đang học, đơn đang dở) và kho thường xanh được chọn theo cộng đồng, và không gửi gì khi không có gì đáng nói.
3. Ba ràng buộc phải biết trước khi bật live
Ba điều này không chặn việc xây (hệ ship ở preview), nhưng chặn việc gửi thật. Mã đã có hàng rào cho cả ba (cột cuối); việc còn lại là của người soạn, ở mục 12 và mục 14.
| # | Ràng buộc | Mã đã làm gì |
|---|---|---|
| 1 | Cloudflare Email Service ghi rõ "chỉ dành cho thư giao dịch" (FAQ, đọc 14.10.2026: "intended only for transactional emails. We plan to support marketing emails and bulk sender tooling in the future"). LUẬT EMAIL của repo cấm nhà cung cấp khác. Hạn mức gửi/ngày tính theo tài khoản, dùng chung với nemo12.com, và không đọc được bằng công cụ hiện có. | live_classes mặc định chỉ quan_he: chỉ thư về việc của chính người nhận được gửi thật. Chuyển live bị API từ chối khi daily_quota = 0. Chưa biết hạn mức thì vòng gửi dừng ở trần mềm 400 thư/ngày UTC của cả api. |
| 2 | Chưa có cơ sở đồng ý nhận thư quảng cáo. Chính sách quyền riêng tư (04/2026) chỉ nêu nhắc bài tập và lịch học trực tiếp. Nghị định 91/2020 đòi thư quảng cáo có đồng ý trước, nhãn [QC] đầu tiêu đề, tối đa 3 thư/24 giờ; Luật Bảo vệ dữ liệu cá nhân 91/2025 đòi đồng ý theo mục đích, nêu kênh, nội dung và tần suất tối đa. | pro_offer chỉ cho người tự bật "Ưu đãi" (ghi thời điểm và consent_version), [QC] gắn ở cổng gửi. Người hệ cũ chỉ được coi là đồng ý khi bấm nút "Có" trên trang xác nhận (POST); bấm một link trong thư không phải đồng ý. Phân loại từng loại ở mục 13, chờ ký. |
| 3 | Uy tín miền dùng chung với login_code. Mọi alias đều ký DKIM conan.school. Một đợt báo spam từ thư gắn kết làm mã đăng nhập vào hố spam. Với 98% Gmail, hai cú bấm "Báo spam" trên một ngày 150 thư đã là ~1,3%, gấp bốn ngưỡng 0,3% Google khuyến nghị. | Cài đặt sender_domain đổi được sang miền con tin.conan.school (48 địa chỉ đã khai trong allowed_sender_addresses). Người hệ cũ chưa từng mở thư cũ mặc định không nhận gì (legacy_cold = skip). Thư reconnect có trần riêng mỗi ngày. |
4. Mục tiêu và chỉ số
Mục tiêu
- Mỗi địa chỉ nhận nhiều nhất một thư gắn kết mỗi ngày, do CSDL cưỡng chế, không do câu SELECT.
- Thư được chọn theo giá trị kỳ vọng (xác suất người đó tương tác × độ có nghĩa của thư), không theo thứ tự cố định; không đủ ngưỡng thì không gửi.
- Nhịp của mỗi người tự thích nghi: người đang dùng nhận dày (thành viên nóng điển hình khoảng 3 thư/tuần, không phải 7), người im lặng nhận thưa dần rồi dừng hẳn; người nhận tự chọn được nhịp và nhóm thư.
- Mọi loại thư sẽ gửi ngày mai tới hộp thư hai người duyệt trước ít nhất 24 giờ, giữ lại được từng loại hoặc cả ngày bằng một cú bấm.
- Mỗi loại thư có người gửi (alias + tên hiển thị) và chiến lược tiêu đề riêng.
- Thư tài khoản không bao giờ bị thư gắn kết chiếm chỗ: mã đăng nhập và biên nhận còn hạn mức dù vòng gửi đang chạy.
Không phải mục tiêu (đợt này)
- Đổi nhà cung cấp gửi thư. Trang tuỳ chọn trong tokyo (đợt này dùng trang có chữ ký, không cần đăng nhập). Chạy A/B tiêu đề. Nhóm đối chứng đo hiệu quả (giai đoạn 2). Thư sinh bằng model.
Chỉ số (đọc ở trang admin, bảng kế hoạch, thư tổng kết vận hành và Postmaster Tools)
| Chỉ số | Ngưỡng tốt | Ngưỡng dừng tăng tải |
|---|---|---|
| Tỉ lệ bấm / thư gửi (click-to-send, chỉ lượt bấm người thật) | ≥ 3% | n/a |
| Huỷ nhận trong ngày (link huỷ + chọn "không nhận" / tắt nhóm / trả lời "Không") | < 0,3% | ≥ 1% hoặc ≥ 3 người |
| Gửi hỏng / thư gửi | < 2% | ≥ 5% |
E_DAILY_LIMIT_EXCEEDED, E_RATE_LIMIT_EXCEEDED | 0 | bất kỳ |
| Tỉ lệ báo spam (Postmaster Tools) | < 0,1% | ≥ 0,3% |
| Người nhận được ít nhất một thư đáng đọc mỗi tuần (trong tầng hoạt động) | ≥ 80% | n/a |
Hệ chỉ tự thấy ba hàng giữa đầu (gửi hỏng, huỷ nhận, hạn mức). Báo spam, bật lại cứng và Gmail hoãn thư đến SAU lúc gửi và chưa có đường về hệ, nên mặc định ramp_pct = 0: trần chỉ tăng khi người vận hành tự tăng sau khi đọc Postmaster Tools.
Tỉ lệ mở chỉ là chỉ số phụ: nó bị thổi lên bởi Apple MPP và bản CC của người vận hành, bị kéo xuống bởi client chặn ảnh, và cột via_proxy của email_opens ghi ngược với tên của nó (xem known-risks).
5. Nguyên tắc thiết kế
- Một khoá cho mỗi người mỗi ngày. Khoá là
lower(trim(email)), không phảiuser_id: 57 người có hai id. Bảng kế hoạch có khoá chính(plan_date, email_key). - Im lặng là kết quả hợp lệ. Thư rỗng gửi cho đủ chỉ tiêu dạy người ta bỏ qua thư của mình, và kéo luôn thư giao dịch xuống hố spam.
- Bấm hơn mở. Hệ thêm theo dõi lượt bấm có chữ ký; lượt mở chỉ là bằng chứng yếu, lượt mở máy (dưới 60 giây, máy quét, MPP) gần như không tính.
- Xem trước là cổng cứng. Không thư nào tới người dùng nếu loại của nó chưa tới cả hai hộp thư duyệt trước ít nhất 24 giờ, và mẫu thư (gồm cả nhánh không tên, phần chữ thuần, người gửi) không đổi kể từ lúc xem trước.
- Mặc định an toàn, người vận hành tự nới. Ship ở
preview;livechỉ gửi nhóm Quan hệ;ramp_pct = 0;daily_quota = 0(chưa biết) chặnlive; người hệ cũ chưa từng mở thư bị bỏ qua. Mỗi lần nới là một quyết định có tên người và thời điểm trongemail_orch_settings. - Bắt đầu nhỏ, gửi rải, xếp người vừa tương tác trước. Ngày đầu tối đa 150 thư, rải từ 08:00 tới 11:00 khoảng 30 thư/phút (người gửi mới dồn thư là kiểu Gmail đáp bằng hoãn 4.7.28); dưới trần, người tầng nóng đứng trước người ngủ đông, và thư reconnect có trần riêng.
- Thư tài khoản không bao giờ bị chặn, nhưng chúng tính vào "một thư hôm nay": ai đã nhận mã đăng nhập hay biên nhận trong ngày thì không nhận thư gắn kết nữa. Ngược lại, thư gắn kết không được tiêu hết hạn mức của thư tài khoản (trần mềm, phần hạn mức chừa lại, thử lại một lần).
- Tắt được trong một phút, không cần deploy: chế độ
off | preview | livevà nút "Dừng mọi thư gắn kết" ở trang admin.
6. Danh mục 18 loại thư
Bốn nhóm, mỗi nhóm một mức cho phép khác nhau (mục 10):
- Quan hệ (
quan_he): về việc của chính người đó (khoá họ học, đơn họ đặt, hạn Pro của họ). - Bản tin (
ban_tin): thông tin của nền tảng mà họ là thành viên, không chào bán. - Giữ liên lạc (
giu_lien_lac): hỏi lại người lâu không gặp có còn muốn nhận thư không. - Ưu đãi (
uu_dai): chào bán. Cần đồng ý riêng, tiêu đề mở đầu bằng[QC].
14 loại mới (NEW_ORCH_KINDS) cộng 4 loại có sẵn của daily.ts (đánh dấu "có sẵn"; mẫu thư của chúng vẫn ở templates.ts). Tiêu đề mẫu dưới đây là tiêu đề thật dựng từ dữ liệu mẫu của EMAIL_CATALOG (14.10.2026), bản có tên / bản không tên. "Nhịp tối đa" là cooldown của loại (KIND_META) cộng luật riêng của generator.
| # | Mã | Tên | Nhóm | Người gửi | Tiêu đề mẫu (có tên / không tên) | Ai nhận, khi nào | Nhịp tối đa |
|---|---|---|---|---|---|---|---|
| 1 | pro_expiring (có sẵn) | Pro sắp hết hạn | Quan hệ | Conan Pro | "Hà ơi, Pro Food còn 14 ngày" / "Pro Food còn 14 ngày" | Pro của một cộng đồng hết hạn trong 14 ngày | 2 mốc mỗi kỳ hạn (14 ngày, 3 ngày) |
| 2 | order_pending | Đơn Pro đang chờ chuyển khoản | Quan hệ | Conan Pro | "Hà ơi, đơn Pro Nhà Food đang chờ chuyển khoản" / "Đơn Pro Nhà Food đang chờ chuyển khoản" | Đơn pending 1-14 ngày, đặt trong tháng này, số tiền còn khớp giá; không gửi người đã có yêu cầu kích hoạt đang xử lý hay đã duyệt (họ đã trả tiền) | 2 lần mỗi đơn, cách 3 ngày |
| 3 | learning_nudge (có sẵn) | Học tiếp khoá đang dở | Quan hệ | Học cùng Conan | "Hà ơi, bạn còn dở "AI cho chủ quán"" / "Bạn còn dở "AI cho chủ quán"" | Khoá đang mở im 3-30 ngày (trên 30 ngày là việc của win_back) | 1 lần mỗi đợt im lặng, cách 5 ngày |
| 4 | weekly_progress | Tuần học của bạn | Quan hệ | Học cùng Conan | "Hà ơi, tuần qua bạn đã học 4 bài" / "Tuần qua bạn đã học 4 bài" | Thứ Hai, đã học ≥ 1 bài (đếm bài, không đếm lượt chuyển trang) trong tuần Chủ nhật tới thứ Bảy vừa qua | 1 lần/tuần |
| 5 | profile_incomplete (có sẵn) | Chọn cộng đồng | Quan hệ | Conan School | "Hà ơi, còn một bước nữa để bắt đầu" / "Còn một bước nữa để bắt đầu" | Tài khoản chưa có cộng đồng nào | 3 lần cách 5 ngày, rồi 1 lần mỗi 60 ngày |
| 6 | event_upcoming | Sự kiện sắp tới | Bản tin | Sự kiện Conan | "Hà ơi, Bàn tròn Marketing tối thứ Năm 15.10, 20:00" / "Bàn tròn Marketing tối thứ Năm 15.10, 20:00" | Sự kiện của trường Conan bắt đầu từ 3 giờ sau giờ gửi tới hết ngày D+10, chưa gửi cho người này; ưu tiên đúng cộng đồng | 1 thư sự kiện/7 ngày |
| 7 | session_upcoming | Buổi học sắp tới | Bản tin | Học cùng Conan | "Khoa ơi, buổi học 10:00 thứ Tư 21.10: AI for Biz: AI for Content" / "Buổi học 10:00 thứ Tư 21.10: AI for Biz: AI for Content" | Buổi học công khai (workshops) của trường Conan trong cùng cửa sổ; người từng học một buổi được ưu tiên | 1 thư/3 ngày, mỗi buổi một lần |
| 8 | whats_new_weekly | Tuần qua có gì mới | Bản tin | Conan School | "Minh ơi, tuần qua Conan có 9 điều mới" / "Tuần qua Conan có 9 điều mới" | Tuần What's New mới nhất có mục đăng trong 9 ngày; tài khoản thấy mục public + member, người hệ cũ chỉ public | 1 lần mỗi tuần |
| 9 | course_new | Khoá mới trong cộng đồng | Bản tin | Nhà + tên cộng đồng | "Bảo ơi, Nhà Retail có khoá mới: Kệ hàng bán được" / "Nhà Retail có khoá mới: Kệ hàng bán được" | Khoá tạo trong 30 ngày, thuộc cộng đồng người đó đã tham gia | 1 lần mỗi khoá, cách 7 ngày |
| 10 | community_weekly (có sẵn) | Tuần này ở Nhà của bạn | Bản tin | Nhà + tên cộng đồng | "Hà ơi, tuần này ở Food có 2 buổi" / "Tuần này ở Food: 2 buổi sắp tới" | Thứ Hai, cộng đồng nhà có sự kiện trong 14 ngày (gồm sự kiện toàn trường) | 1 lần/tuần |
| 11 | unit_story | Một câu chuyện trong nghề | Bản tin | Ran ở Conan | "Hà ơi, kinh tế đơn vị: Đưa đủ chi phí vào" / "Kinh tế đơn vị: Đưa đủ chi phí vào" | Kho SCQA của cộng đồng nhà, rồi cộng đồng đã tham gia, rồi cộng đồng suy từ workshop hệ cũ; không có kho chung dự phòng | cách 2 ngày, không lặp |
| 12 | book_chapter | Một chương sách đáng đọc | Bản tin | Thư viện Conan | "Khoa ơi, chương 1 của Định giá cho quán nhỏ" / "Chương 1 của Định giá cho quán nhỏ" | Sách của trường Conan trên kệ cộng đồng của người đó, có chương 1 | cách 5 ngày, không lặp |
| 13 | playbook_pick | Bài playbook đáng đọc | Bản tin | Thư viện Conan | "Trâm ơi, 7 bước viết brief cho AI" / "7 bước viết brief cho AI" | Bài playbook công khai mới nhất mà người đó chưa nhận | cách 4 ngày, không lặp |
| 14 | jtbd_case | Khách hàng thật sự cần gì | Bản tin | Ran ở Conan | "Lan ơi, người mua đồ uống hằng ngày thật ra cần gì?" / "Người mua đồ uống hằng ngày thật ra cần gì?" | Job của bộ JTBD miễn phí trong cộng đồng của người đó | cách 5 ngày, không lặp |
| 15 | journey_day | Chuỗi 30 thư làm quen | Bản tin | Nhà + tên cộng đồng | "Hà ơi, chào mừng tới Nhà Food" / "Chào mừng tới Nhà Food" (thư 1) | Tài khoản tokyo đã có cộng đồng nhà, chưa nhận đủ 30 thư. Mỗi ngày đúng một ứng viên: thư có số nhỏ nhất chưa nhận, tranh khe với mọi loại khác theo điểm; tài khoản ≤ 30 ngày tuổi được urgency 1,25. Người chưa có cộng đồng và người chỉ có ở hệ cũ không nhận | 1 thư/ngày (cooldown 1), tối đa 30 thư cả đời, thư N chỉ đi sau thư N-1 |
| 16 | reconnect | Hỏi lại người hệ cũ | Giữ liên lạc | Conan School | "Hà ơi, bạn còn muốn nhận thư từ Conan?" / "Bạn còn muốn nhận thư từ Conan School?" | Người chỉ có ở hệ cũ, chưa trả lời. Từng mở/bấm thư cũ: tối đa 2 lần. Chưa từng mở: không gửi (mặc định) hoặc 1 lần | 2 lần cách 10 ngày, rồi dừng |
| 17 | win_back | Lâu rồi không gặp | Giữ liên lạc | Conan School | "Hà ơi, 3 điều mới từ lần cuối bạn ghé" / "3 điều mới từ lần cuối bạn ghé Conan" | Tài khoản im ≥ 45 ngày, chỉ khi có ít nhất một điều mới thật (không nhắc Pro hay giá) | 2 lần, cách 60 ngày |
| 18 | pro_offer | Giá Pro sắp tăng | Ưu đãi | Conan Pro | "[QC] Hà ơi, giá Pro tăng từ 01.11 ở Nhà Food" / "[QC] Giá Pro tăng từ 01.11 ở Nhà Food" | 7 ngày cuối tháng, giá tháng sau cao hơn thật; người đã bật Ưu đãi, đang hoạt động, ở mức Free; không có đơn chờ ở giá hiện hành | 1 lần mỗi cộng đồng mỗi tháng (cooldown 25 ngày) |
Thêm một loại cho người vận hành: orch_preview_digest ("Kế hoạch thư 15.10: 127 thư, 2 loại (chạy thử)"), bảng kế hoạch ngày mai, gửi riêng tới từng người duyệt (mục 8). Nó không phải thư gắn kết, không bị CC, và tự loại khỏi thống kê của thư tổng kết.
Không đưa vào, có lý do (đo 14.10.2026): buổi học lớp (class_sessions 0 buổi sắp tới, ngày cuối 02.09.2026); thảo luận cộng đồng (162 bài đều là dữ liệu mẫu hư cấu, gửi thư "thảo luận sôi nổi tuần này" là trình bày hoạt động giả như thật); tin tức, thông báo, câu chuyện người học, mentor, album (không có gì mới từ tháng 3-8); video (đang để Unlisted theo chủ ý của người soạn). Buổi học công khai (workshops) có vào, thành loại riêng session_upcoming, với một giới hạn: kế hoạch ngày D lập từ 07:07 ngày D-1, nên buổi tạo sau lúc đó không lọt vào thư ngày D (buổi "AI for Biz" tạo ngay trong ngày sẽ không bao giờ có thư, trừ khi được tạo trước ít nhất hai ngày).
Chuỗi 30 thư làm quen (journey_day, từ 14.10.2026). PR #351 (30 thư chào mừng, người gửi là alias của từng cộng đồng) đóng ngày 14.10.2026; nội dung của nó vào Nhịp thư thành loại thứ 15 ở bảng trên. Khác bản PR ở chỗ quan trọng nhất: chuỗi không chiếm khe một-thư-mỗi-ngày suốt 30 ngày liền. Mỗi ngày, thư kế tiếp của chuỗi chỉ là MỘT ứng viên, tranh khe với sự kiện, nhắc học, câu chuyện nghề... theo điểm, chịu tầng, mỏi, tuỳ chọn của người nhận và cổng xem trước 24 giờ; hôm nào loại khác thắng thì chuỗi chờ. Giữa hai thư của chuỗi có thể cách một ngày hay vài tuần, nên chữ trong thư không nói gì về lịch ("mỗi sáng", "một tuần rồi"), chỉ nói vị trí ("thư 7/30"). Chuỗi thuộc nhóm Bản tin: với live_classes = quan_he mặc định, nó chỉ được lập kế hoạch ở preview và chỉ gửi thật khi người vận hành bật nhóm Bản tin (sau khi Cloudflare xác nhận, Q1). Kế hoạch diễn tập cho ngày 16.10.2026 trên dữ liệu thật có 7 thư chuỗi; với base 0,65 chỉ ra 2, vì EV của thành viên không còn mới rơi dưới ngưỡng tầng nóng 0,08, nên base là 0,8. Người nhận tắt riêng chuỗi bằng nhóm "Chuỗi thư làm quen" ở trang tuỳ chọn (mục 9) mà không mất nhắc học hay thư khác.
Chiến lược tiêu đề
- Tên ở đầu, dùng
tenGoi(), luôn có bản không tên. Gmail điện thoại cắt ở ~35 ký tự: phần quan trọng nằm trong 35 ký tự đầu. Gác: tiêu đề mọi loại mới ≤ 80 ký tự, cả hai bản. - Cụ thể hơn hấp dẫn: con số, ngày giờ, tên khoá. Câu chuyện nghề dùng "tên khoá: tên bài", KHÔNG dùng câu hỏi SCQA: câu hỏi chỉ có nghĩa sau khi đọc tình huống (276/639 câu ngắn hơn 35 ký tự, như "Hỏi gì?"; bản xem trước đầu tiên ra "Chi ơi, đưa vào việc thế nào?"). Không VIẾT HOA, không "!!!", không emoji, không "Miễn phí"/"Ưu đãi sốc" (từ ngữ kích hoạt bộ lọc).
- Tiêu đề đúng nội dung (Nghị định 91). Thư Ưu đãi bắt buộc
[QC]đầu tiêu đề, do cổng gửi tự gắn theo nhóm (isPromotionalKind), không để từng mẫu tự nhớ. - Dòng xem trước (preheader) cho mọi thư mới: câu thứ hai của tiêu đề, hiện cạnh tiêu đề trong hộp thư. Gác kiểm mọi mẫu mới có nó.
7. Người gửi (alias) và tên hiển thị
| Khoá | Địa chỉ | Tên hiển thị | Dùng cho | Vì sao tách |
|---|---|---|---|---|
hello | [email protected] | Conan School | thư tài khoản, profile_incomplete, whats_new_weekly, reconnect, win_back, bảng kế hoạch | Tên người ta đã biết; thư hỏi lại người cũ phải đến từ cái tên họ từng đăng ký |
hoc | [email protected] | Học cùng Conan | learning_nudge, weekly_progress, session_upcoming | Tách luồng "việc học của tôi" khỏi luồng tin chung, để người ta nhận ra trong hộp thư |
sukien | [email protected] | Sự kiện Conan | event_upcoming | Thư có hạn chót (giờ bắt đầu); tên nói luôn đây là lịch |
ran | [email protected] | Ran ở Conan | unit_story, jtbd_case | Ran là trợ lý mà thành viên đã gặp trong tokyo; giọng một người kể chuyện tăng tỉ lệ mở hơn tên thương hiệu |
thuvien | [email protected] | Thư viện Conan | book_chapter, playbook_pick | Bài đọc dài, đọc lúc rảnh, tách khỏi thư có hạn chót |
pro | [email protected] | Conan Pro | pro_expiring, order_pending, pro_offer | Mọi thứ liên quan tiền ở một chỗ, dễ nhận ra, dễ lọc |
nha | 18 alias theo cộng đồng (food@, marketing@, ai@, fitness@, travel@, people@, cs@...) | Nhà <tên> · Conan | course_new, community_weekly, journey_day | Danh tính cộng đồng, lấy lại từ PR #351; tên ngắn vì Gmail điện thoại chỉ hiện ~20 ký tự tên người gửi. Không biết cộng đồng thì rơi về hello |
Hai miền gửi. Cài đặt sender_domain chọn conan.school (mặc định) hoặc tin.conan.school cho mọi thư của Nhịp thư; thư tài khoản (mã đăng nhập, biên nhận) luôn đi từ conan.school. Miền con là để tách uy tín: Cloudflare khuyên tách miền con cho thư tiếp thị, và một đợt báo spam từ thư gắn kết khi đó không kéo mã đăng nhập theo. Miền con phải được bật Email Sending trước khi đổi cài đặt; đổi sớm thì mọi lượt gửi hỏng E_SENDER_* và loại đó tự bị giữ trong ngày.
Trên Cloudflare: không cần tạo từng alias; mọi địa chỉ thuộc miền đã bật Email Sending đều gửi được. Repo khoá danh sách bằng allowed_sender_addresses trong wrangler.toml (24 địa chỉ × 2 miền = 48): mã không thể gửi từ một địa chỉ nằm ngoài danh sách, và npm run test:orchestrator so danh sách đó với bảng người gửi trong mã, từng địa chỉ, cả hai chiều.
Trả lời thư: MX của conan.school là Google Workspace, không phải Cloudflare Email Routing. Mọi thư từ địa chỉ khác [email protected] mang Reply-To: [email protected], nên người nhận trả lời luôn tới một hộp thư có người đọc. Muốn trả lời tới thẳng alias thì người soạn tạo alias tương ứng trong Google Admin (mục 12, Q4).
8. Xem trước 24 giờ và điều chỉnh
D-1 07:07 lượt mỗi giờ đầu tiên lập kế hoạch ngày D (mọi người, mọi loại; ở preview lập đủ mọi nhóm)
D-1 ~07:10 gửi tới [email protected] và [email protected]:
1 thư MẪU cho MỖI loại có người được chọn (kể cả loại chỉ có dòng vượt trần),
từ đúng alias, đúng tiêu đề, dựng bằng dữ liệu THẬT của một người nhận
1 thư "Kế hoạch thư ngày D" (bảng theo loại, nút Giữ lại từng loại / cả ngày)
dời giờ gửi của từng loại tới giờ tròn sau (lúc xem trước loại đó + 24 giờ)
D-1 08:07 lượt mỗi giờ thử lại phần còn thiếu (lập kế hoạch hỏng, loại xem trước hỏng,
...20:07 tối đa 3 lần mỗi loại); sau 20:07 không lập kế hoạch cho ngày mai nữa
D-1 → D người soạn đọc, bấm "Giữ lại" nếu cần, hoặc sửa mẫu (PR, deploy): mẫu đổi thì
loại đó tự bị giữ hôm đó, và xem trước của hôm sau được gửi lại bằng mẫu mới
D 08:00 gửi thật theo thứ tự ưu tiên, rải 08:00-11:00, ~30 thư/phút,
-11:00 chỉ dòng có send_at ≥ lúc xem trước + 24 giờ
D 21:00 giờ gửi muộn nhất; dòng bị dời quá giờ này bỏ với lý do preview_window,
dòng còn chờ sau 22:00 thành expired- Thư mẫu mang dải chữ nhỏ ở đầu "Bản xem trước: sẽ gửi ngày dd.mm, từ 08:00 cho N người" và đuôi tiêu đề " · xem trước dd.mm"; phần đầu tiêu đề giống hệt thư thật (không có tiền tố
[BẢN SAO). - Thư mẫu không chạm tới người nhận thật. Mẫu thư chỉ chứa chỗ giữ cho link theo người nhận (trang tuỳ chọn, nút "Có / Không" của reconnect); với thư mẫu, cổng gửi thay chúng bằng trang "đây là bản xem trước", nên người duyệt bấm "Không, đừng gửi nữa" không huỷ nhận hộ ai. Bảng kế hoạch che địa chỉ người nhận mẫu (
a***@gmail.com). - Trang admin Nhịp thư cho xem toàn bộ danh sách ngày mai và mở từng thư của từng người (dựng lúc xem), nên "xem trước mọi thư" không đòi gửi 1.264 × 2 thư mỗi ngày. Nút "Gửi bộ mẫu" gửi dữ liệu mẫu của cả 18 loại tới hai hộp thư duyệt (tối đa một lần mỗi 10 phút), để đọc mẫu ngay sau deploy mà không chờ kế hoạch ngày mai.
- Một loại chứa 30 thư (
journey_day): thư mẫu mỗi ngày chỉ cho thấy MỘT thư của chuỗi. Để sửa thư 12 sau lúc xem trước không lọt qua cổng, HTML của mọi thư chuỗi mang một dấu nội dung băm cả 30 thư (SDD mục 9): sửa bất kỳ thư nào là đổi dấu vân tay, loại bị giữ tới lần xem trước sau. Người duyệt vẫn chỉ thấy một thư mỗi ngày; xem đủ 30 thư ở admin là việc giai đoạn sau. - Nút "Giữ lại" trong thư dẫn tới trang xác nhận có nút bấm (POST); máy quét link chỉ tải trang (GET) nên không giữ nhầm được. Link hết hạn lúc 23:00 ngày D.
9. Tuỳ chọn của người nhận
Mọi thư gắn kết có chân thư "Đổi nhịp nhận thư · Huỷ nhận thư". "Đổi nhịp" đứng trước: người thấy thư hơi dày thường chỉ muốn thưa đi, và nếu lựa chọn duy nhất là huỷ thì họ huỷ, hoặc tệ hơn, bấm "Báo spam". Trang đổi nhịp có chữ ký, không cần đăng nhập, cho chọn:
- Nhịp: Tự động (tối đa 1 thư/ngày, chỉ khi có điều đáng đọc) · Tối đa 1 thư/tuần · Chỉ thư về việc của tôi (khoá học, đơn, hạn Pro) · Không nhận thư nào ngoài thư tài khoản.
- Nhóm (tích là nhận): Học tập · Sự kiện và buổi học · Bài đọc và câu chuyện · Tin tuần · Chuỗi thư làm quen. Mỗi nhóm hiện tên các loại thư thuộc nó, lấy từ
KIND_META, nên mô tả luôn khớp cách cổng gửi lọc. "Chuỗi thư làm quen" (lam_quen, thêm 14.10.2026) chỉ chứajourney_day, và là nhóm RIÊNG có chủ ý: người thấy chuỗi dày phải tắt được nó mà không mất luôn nhắc học hay thư khoá mới. Người đã lưu tuỳ chọn trước ngày đó không cólam_quentrong danh sách tắt, nên vẫn nhận chuỗi cho tới khi tự bỏ tích, kể cả người đã tắt cả bốn nhóm cũ mà giữ nhịp "Tự động" (nhịp "Chỉ thư về việc của tôi" thì chặn chuỗi, vì chuỗi là Bản tin). - Ưu đãi (mặc định TẮT): nhãn nêu kênh (email), nội dung (ưu đãi gói Pro), tần suất tối đa (1 thư/tháng), nhãn
[QC], và cách rút lại. Bật là đồng ý nhận thư quảng cáo: ghi thời điểm và phiên bản câu chữ (consent_version).
Lựa chọn được thi hành ở ba chỗ: lúc lập kế hoạch, lúc gửi (đọc lại), và ở cổng gửi cho mọi thư gắn kết, kể cả thư của daily.ts; một lựa chọn trên trang mà một vòng gửi bỏ qua là một lời hứa không thi hành. "Không nhận thư nào" ghi huỷ nhận cho mọi id của địa chỉ; chọn lại nhịp khác gỡ huỷ nhận do chính người đó chọn (không bao giờ gỡ suppressed).
Link "Huỷ nhận thư" mở trang xác nhận có nút (GET không ghi gì); nút huỷ một chạm của Gmail (List-Unsubscribe-Post, RFC 8058) vẫn POST thẳng và huỷ toàn bộ thư gắn kết ngay lập tức.
10. Ai được nhận nhóm nào
| Nhóm | Tài khoản hệ mới (users) | Người chỉ có ở hệ cũ | Người chưa đủ 16 tuổi |
|---|---|---|---|
| Quan hệ | Có | Thực tế không: mọi generator Quan hệ chỉ cho tài khoản (đơn, ghi danh, cộng đồng gắn với users.id, link đều về tokyo) | Có |
| Bản tin | Có, trừ khi tắt nhóm | Chỉ sau khi bấm "Có" trên trang xác nhận của thư reconnect (POST). Bấm một link trong thư không tính | Không |
| Giữ liên lạc | win_back | reconnect: người từng mở/bấm thư cũ tối đa 2 lần cách 10 ngày; người chưa từng mở không gửi (legacy_cold = skip) hoặc 1 lần (once) | Không |
| Ưu đãi | Chỉ người đã bật "Ưu đãi" | Không (pro_offer chỉ cho tài khoản) | Không |
- "Người chưa đủ 16 tuổi" là người học có dòng
learner_guardians, hoặc người hệ cũ có năm sinh cho ra dưới 16 tuổi vào ngày gửi. Đồng ý của chính họ không thay được đồng ý của cha mẹ. - Chính sách người hệ cũ là một cài đặt (
legacy_policy):reconnect(mặc định),open(gửi như thành viên; API đòi xác nhận riêng vì đó là hơn một nghìn địa chỉ chưa từng đồng ý),off. - Ở chế độ
live, chỉ các nhóm tronglive_classes(mặc địnhquan_he) tranh khe; chế độpreviewlập kế hoạch đủ mọi nhóm để người duyệt đọc được mọi loại trước khi quyết bật nhóm nào.
11. User story
COMM-ORCH-01: tối đa một thư gắn kết mỗi ngày cho mỗi địa chỉ
Là người nhận, tôi muốn nhận nhiều nhất một thư gắn kết mỗi ngày, kể cả khi tôi có hai tài khoản.
- Given một địa chỉ có hai
user_id(hệ cũ và hệ mới) When lập kế hoạch ngày D Then chỉ có một dòng(D, email_key); dòng thứ hai bị CSDL từ chối. - Given tôi đã nhận mã đăng nhập lúc 07:30 ngày D When tới giờ gửi thư gắn kết của tôi Then thư bị bỏ với lý do
already_mailed_today.
COMM-ORCH-02: chọn thư theo giá trị kỳ vọng
Là người nhận, tôi muốn thư tôi nhận là thư khả năng cao tôi thấy có ích.
- Given tôi có khoá đang dở và cộng đồng có sự kiện sau 3 ngày When lập kế hoạch Then mỗi ứng viên có điểm
p × v × mỏi, thư điểm cao nhất được chọn, và lý do chọn (tầng, p, v, mỏi, EV so với ngưỡng) cùng tối đa 3 ứng viên thua được ghi vào dòng kế hoạch. - Given không ứng viên nào có EV trung bình vượt ngưỡng của tầng của tôi When lập kế hoạch Then tôi không nhận thư nào hôm đó (một lần rút ngẫu nhiên may mắn không được mua một thư).
COMM-ORCH-03: nhịp tự thích nghi và dừng hẳn với người im lặng
- Given tôi nhận 3 thư gắn kết liền mà không bấm, không mở (bằng chứng người thật) When lập kế hoạch Then tôi tụt một tầng (khoảng cách giữa hai thư dài ra, ngưỡng cao lên), dù hôm qua tôi có vào web.
- Given tôi bỏ qua 6 thư gắn kết liền và không có dấu hiệu nào (web, học, thư) hơn 30 ngày When lập kế hoạch Then tôi ở tầng
dungvà không nhận thư gắn kết nào cho tới khi tôi tương tác lại.
COMM-ORCH-04: người nhận tự chọn nhịp và nhóm
- Given tôi chọn "Tối đa 1 thư/tuần" và tắt "Bài đọc và câu chuyện" When lập kế hoạch các ngày sau Then khoảng cách giữa hai thư của tôi ≥ 7 ngày và không có
unit_story,book_chapter,playbook_pick,jtbd_case. - Given tôi bật "Ưu đãi" Then thời điểm và phiên bản câu chữ đồng ý được ghi; trước đó tôi không bao giờ nhận
pro_offer. - Given tôi đổi nhịp sau khi kế hoạch ngày mai đã lập When tới giờ gửi Then cổng gửi đọc lại lựa chọn mới và bỏ dòng với lý do
opted_out.
COMM-ORCH-05: xem trước 24 giờ là cổng cứng
Là người soạn, tôi muốn thấy mọi loại thư sẽ gửi ngày mai, trong hộp thư của tôi, trước 24 giờ.
- Given kế hoạch ngày D có 5 loại When xem trước chạy Then mỗi hộp thư duyệt nhận 1 thư kế hoạch + 5 thư mẫu từ đúng alias, đúng tiêu đề.
- Given gửi thư mẫu của loại X tới một trong hai hộp thư duyệt bị lỗi When tới giờ gửi ngày D Then không dòng nào của loại X được gửi; lượt mỗi giờ thử lại tối đa 3 lần, và lỗi hiện ở bảng kế hoạch.
- Given mẫu của loại X đổi (deploy) sau lúc xem trước, kể cả chỉ nhánh tiêu đề không tên, phần chữ thuần hay người gửi When tới giờ gửi Then mọi dòng của X hôm đó bị bỏ với lý do
template_changed, và xem trước của X cho ngày mai được gửi lại bằng mẫu mới. - Given thư mẫu của
reconnectdựng từ dữ liệu của một người hệ cũ thật When người duyệt bấm "Không, đừng gửi nữa" trong thư mẫu Then trang "đây là bản xem trước" hiện ra; không gì được ghi cho người thật đó.
COMM-ORCH-06: giữ lại bằng một cú bấm, không bị máy quét bấm hộ
- Given thư kế hoạch có nút "Giữ lại loại này" When máy quét link tải link đó (GET) Then chỉ hiện trang xác nhận; không gì bị giữ.
- When tôi bấm nút trên trang xác nhận (POST, chữ ký đúng, chưa hết hạn) Then loại đó của ngày D bị giữ, ghi người giữ và lúc giữ; thả giữ làm ở admin.
COMM-ORCH-07: người gửi theo loại, trả lời tới người thật
- Given thư
event_upcomingThen From là "Sự kiện Conan" [email protected], Reply-To là [email protected], và sổ gửi ghi địa chỉ From. - Given Cloudflare từ chối một alias (
E_SENDER_*) Then cả loại đó bị hệ giữ trong ngày (held_by = system), không lặng lẽ đổi sang hello@.
COMM-ORCH-08: người hệ cũ được hỏi trước
- Given
legacy_policy='reconnect'và tôi chỉ có ởlegacy_users, từng mở thư cũ When lập kế hoạch Then loại duy nhất tôi có thể nhận làreconnect(tối đa 2 lần cách 10 ngày), cho tới khi tôi bấm "Có" trên trang xác nhận. - Given tôi bấm vào một bài đọc trong thư nhưng chưa bấm "Có" Then tôi vẫn chưa được coi là đồng ý.
- Given tôi chưa từng mở thư nào của Conan và
legacy_cold='skip'Then tôi không nhận thư nào.
COMM-ORCH-09: tăng tải dần và tự dừng
- Given ngày đầu
liveThen tối đacap_start(150) thư; vớiramp_pct = 0mặc định, trần giữ nguyên tới khi người vận hành tự tăng. - Given
ramp_pct > 0Then trần ngày sau là trần hôm trước × (1 + ramp%) chỉ khi ngày live gần nhất đã xong khoẻ (không chạm hạn mức, gửi hỏng < 5%, huỷ nhận < 3 người và < 1%), tính lại lúc bắt đầu gửi, không phải lúc lập kế hoạch. - Given Cloudflare trả
E_DAILY_LIMIT_EXCEEDEDThen vòng gửi dừng ngay, các dòng còn lại thànhdeferred, chế độ tự vềpreview, trần lần live sau hạ còn 80% số đã gửi.
COMM-ORCH-10: theo dõi lượt bấm an toàn
- Given một link trong thư gắn kết Then link đi qua
/api/system/email/c/<delivery>có chữ ký; đích chỉ được làhttps://*.conan.school; sai chữ ký hay sai đích thì về trang chủ, không bao giờ là cửa chuyển hướng mở. - Given lượt bấm là HEAD, không có User-Agent, có dấu máy quét, hoặc trong 10 giây đầu sau khi gửi Then nó được ghi
is_bot = 1và không tính là tương tác.
COMM-ORCH-11: tắt trong một phút
- Given người soạn chuyển chế độ sang
offở admin Then không lập kế hoạch, không gửi; chuyểnpreviewthì lập kế hoạch và gửi xem trước nhưng không gửi người dùng. - Given người soạn bấm "Dừng mọi thư gắn kết" Then Nhịp thư về
offVÀ vòngdaily.tstắt, trong cùng một giao dịch; lô đang gửi dừng từ lô kế tiếp (tối đa 20 thư); thư tài khoản vẫn đi.
COMM-ORCH-12: một vòng sở hữu khe "một thư mỗi ngày"
- Given chế độ
liveThensendDailyMemberEmails(daily.ts) không gửi gì; bốn loại của nó là ứng viên trong Nhịp thư. - Given chế độ
previewhoặcoffThendaily.tschạy như cũ (trừ khi đã bị nút dừng tắt; bật lại ở admin).
COMM-ORCH-13: không nhận lại cùng một thư hai ngày liền
Là người nhận, tôi không muốn nhận cùng câu chuyện hay cùng thư hỏi lại hai sáng liền.
- Given kế hoạch ngày D lập lúc 07:07 ngày D-1, khi thư ngày D-1 chưa đi When lập kế hoạch ngày D Then dòng kế hoạch chưa gửi của các ngày trước được tính như thư đã gửi cho khoảng cách, cooldown, chống lặp và mỏi (nhưng không tính vào chuỗi im lặng).
- Given cùng (địa chỉ, loại, mục) đã được gửi When tới giờ gửi Then CSDL từ chối lần thứ hai (
email_sent_refs), dòng bị bỏ với lý doalready_sent_ref.
COMM-ORCH-14: người chưa đủ 16 tuổi chỉ nhận thư về việc học của chính họ
- Given tôi có phụ huynh đăng ký (
learner_guardians) hoặc năm sinh cho ra dưới 16 tuổi Then tôi có thể nhận thư Quan hệ (nhắc học, tuần học), không bao giờ Bản tin, Giữ liên lạc hay Ưu đãi.
COMM-ORCH-15: thư tài khoản không bị thư gắn kết chiếm hạn mức
Là người dùng, tôi muốn mã đăng nhập tới cả khi hệ đang gửi thư gắn kết.
- Given hạn mức tài khoản chưa biết (
daily_quota = 0) When vòng gửi chạy Then nó dừng khi tổng thư của api trong ngày UTC chạm 400, các dòng còn lại thànhdeferredvới lý dosoft_ceiling. - Given hạn mức đã nhập Then trần thư gắn kết ≤ 60% hạn mức trừ ngày đông nhất 30 ngày qua của thư không thuộc Nhịp thư, và vòng gửi dừng ở 80% hạn mức.
- Given
login_codegặpE_RATE_LIMIT_EXCEEDEDThen cổng gửi thử lại một lần sau 1,5 giây (thư gắn kết thì không, vòng gửi tự lùi).
COMM-ORCH-16: GET không bao giờ ghi
- Given máy quét link của doanh nghiệp tải mọi link trong thư When nó tải link huỷ, link "Có / Không", link tuỳ chọn hay link giữ lại Then chỉ hiện trang xác nhận; chỉ nút bấm (POST) mới ghi.
- Given Gmail gửi POST huỷ một chạm (RFC 8058,
u,tở query, không cookie, không Origin) Then huỷ được ghi và trả 200 HTML, không chuyển hướng; ghi hỏng thì trả 503, không 200.
COMM-ORCH-17: chỉ nhóm được bật mới gửi thật
- Given
mode = livevàlive_classes = quan_heThen chỉ thư Quan hệ tranh khe và được gửi; dòng của nhóm khác (từ kế hoạch lập lúc còn preview) bị bỏ với lý doclass_not_live. - Given người vận hành chuyển sang
livekhidaily_quota = 0Then API từ chối (422) và nêu rõ cần nhập hạn mức.
COMM-ORCH-18: link đúng trang cho đúng người
- Given người hệ cũ (không có tài khoản tokyo) Then mọi link của họ về trang công khai ở www; tài khoản vào tokyo khi trang đó có ở tokyo.
- Given thư sự kiện cho một tài khoản Then link là
tokyo.conan.school/online-events/:slug, không phải/events/:slug(trang chuỗi sự kiện, ra "Series không tồn tại"). - Given thư
unit_storyThen số trong/courses/:slug/units/:nlà vị trí của unit (đếm từ 1), không phảisort_order.
COMM-ORCH-19: chuỗi 30 thư làm quen đi đúng thứ tự, tranh khe như mọi loại khác
Là thành viên mới đã chọn cộng đồng, tôi muốn được dẫn qua những gì Conan có theo một thứ tự hợp lý, nhưng không phải nhận một thư chuỗi mỗi sáng bất kể còn thư nào đáng đọc hơn.
- Given tôi có cộng đồng nhà và đã nhận thư 1-4 của chuỗi When lập kế hoạch Then ứng viên
journey_daycủa tôi là thư 5 (refjourney:5), một ứng viên như mọi loại khác: nếu sự kiện hay nhắc học có điểm cao hơn thì thư 5 chờ hôm sau, không hôm nào tôi nhận hai thư. - Given thư 5 có trong kế hoạch hôm nay nhưng bị bỏ lúc gửi (ví dụ tôi vừa nhận mã đăng nhập sáng nay,
already_mailed_today), và kế hoạch ngày mai, lập trước giờ gửi, đã chọn thư 6 When tới giờ gửi ngày mai Then thư 6 bị bỏ với lý dono_longer_valid(thư N chỉ đi khi thư N-1 đã đi), và lần lập kế hoạch kế tiếp chọn lại thư 5: thư bị bỏ được gửi lại, tôi không bao giờ đọc thư 30 trước thư 29. - Given tài khoản của tôi chưa có cộng đồng nào, hoặc tôi chỉ có ở
legacy_usersWhen lập kế hoạch Then tôi không có ứng viênjourney_daynào (người chưa có cộng đồng là việc củaprofile_incomplete); chuỗi bắt đầu từ thư 1 khi tôi chọn xong cộng đồng. - Given tôi bỏ tích "Chuỗi thư làm quen" ở trang Đổi nhịp nhận thư When lập kế hoạch và khi gửi Then tôi không nhận thư nào của chuỗi nữa (
opted_outnếu dòng đã lập), còn nhắc học, sự kiện, bài đọc và tin tuần vẫn như cũ. - Given tôi đã nhận đủ 30 thư Then không còn ứng viên
journey_day(maxLifetime = 30).
12. Quyết định chờ người soạn (trước khi bật live)
| # | Câu hỏi | Khuyến nghị | Mặc định đã ship |
|---|---|---|---|
| Q1 | Dùng Cloudflare cho thư không giao dịch dù FAQ nói chỉ giao dịch? | Viết cho Cloudflare (Support/Discord) xin xác nhận bằng văn bản + hạn mức/ngày; trong lúc chờ chỉ gửi thật nhóm Quan hệ | live_classes = quan_he; mọi nhóm lập kế hoạch ở preview; chưa gửi ai |
| Q2 | Người hệ cũ (1.090 đang hoạt động) | reconnect: chỉ hỏi người từng mở/bấm thư cũ (533), tối đa 2 lần; người chưa từng mở thì không gửi | legacy_policy = reconnect, legacy_cold = skip, trần reconnect min(50, 25% trần ngày) |
| Q3 | Đồng ý nhận ưu đãi | Cập nhật Chính sách quyền riêng tư nêu mục đích + tần suất tối đa; chỉ gửi pro_offer cho người bật "Ưu đãi" | pro_offer chỉ cho người đã đồng ý, có consent_version |
| Q4 | Alias trong Google Workspace | Tạo alias hoc@, sukien@, ran@, thuvien@, pro@ (và alias Nhà nếu muốn) cho hộp hello@ (Google Admin → Users → hello → Alternate emails) | Reply-To: hello@ cho mọi alias |
| Q5 | DNS | Thêm SPF ở gốc cho Google, bật DKIM của Workspace, thêm rua vào DMARC, đăng ký Postmaster Tools | Không đổi (token chỉ đọc zone) |
| Q6 | PR #351 (chuỗi 30 thư chào mừng) | Đóng PR; đưa nội dung 30 ngày vào Nhịp thư thành loại journey_day để nó tranh khe với các loại khác thay vì chiếm khe 30 ngày liền | Đã làm 14.10.2026: PR đóng, nội dung thành journey_day (mục 6, dòng 15), đã kiểm lại từng câu với production. Thuộc Bản tin, nên chưa gửi thật cho tới khi bật nhóm đó; người soạn còn phải ký dòng 15 của mục 13 |
| Q7 | Xác minh [email protected] làm địa chỉ đích ở Cloudflare | Nên: thư mẫu và bảng kế hoạch gửi tới đó mỗi ngày | Chưa xác minh |
| Q8 | Tách uy tín bằng tin.conan.school | Bật Email Sending cho miền con rồi chuyển sender_domain, trước khi gửi thật bất kỳ nhóm nào ngoài Quan hệ | sender_domain = conan.school; 48 địa chỉ hai miền đã khai |
| Q9 | Phân loại pháp lý từng loại (mục 13) | Người soạn ký từng dòng; loại nào ký "là quảng cáo" thì chuyển sang nhóm Ưu đãi | Theo cột "đề xuất"; chưa nhóm nào ngoài Quan hệ được gửi thật |
| Q10 | Tốc độ tăng tải | Giữ ramp_pct = 0; tăng trần tay sau khi đọc Postmaster Tools ít nhất 3 ngày sạch | cap_start = 150, ramp_pct = 0, cap_max = 1300 |
13. Phân loại pháp lý từng loại thư
Vì sao có bảng này: Nghị định 91/2020 chỉ đòi đồng ý trước, nhãn [QC] và giới hạn tần suất với thư điện tử quảng cáo. Loại nào là quảng cáo thì cả nhóm đó đổi luật chơi, mà rà soát thiết kế (14.10.2026) thấy bản đầu chỉ coi pro_offer là quảng cáo, trong khi course_new giới thiệu sản phẩm mới của một nền tảng có thu phí và win_back kể "điều mới" cho người không còn ghé. Ranh giới này là phán đoán pháp lý, không phải phán đoán kỹ thuật, nên agent chỉ đề xuất; người soạn ký từng dòng trước khi nhóm đó lên live.
| # | Mã | Nhóm | Có phải thư quảng cáo theo Nghị định 91? (đề xuất) | Lý do, và điều kiện để giữ nguyên phân loại | Người soạn ký duyệt |
|---|---|---|---|---|---|
| 1 | pro_expiring | Quan hệ | Không | Thông báo về dịch vụ người nhận đang dùng (hạn gói họ đã mua). Không được kèm giá mới hay khuyến mãi gia hạn | Chưa ký |
| 2 | order_pending | Quan hệ | Không | Về giao dịch chính người nhận khởi tạo (số tiền, mã đơn, mã chuyển khoản) | Chưa ký |
| 3 | learning_nudge | Quan hệ | Không | Về khoá người nhận đang học | Chưa ký |
| 4 | weekly_progress | Quan hệ | Không | Số liệu học của chính người nhận | Chưa ký |
| 5 | profile_incomplete | Quan hệ | Không | Một bước còn thiếu trong tài khoản của họ | Chưa ký |
| 6 | event_upcoming | Bản tin | Ranh giới: không, nếu sự kiện miễn phí và thuộc cộng đồng họ đã tham gia hoặc toàn trường | Sự kiện có phí, hay của cộng đồng họ chưa vào, nghiêng về chào bán dịch vụ | Chưa ký |
| 7 | session_upcoming | Bản tin | Ranh giới: có, khi buổi học thu phí (price > 0) | Mời dự một buổi học trả phí là chào bán dịch vụ; buổi miễn phí thì nghiêng về bản tin. Nếu ký "có" thì chỉ gửi buổi miễn phí, hoặc chuyển nhóm Ưu đãi | Chưa ký |
| 8 | whats_new_weekly | Bản tin | Không, nếu không mục nào chào bán | Tin về thay đổi của nền tảng họ là thành viên. Mục nhắc Pro, giá, học phí thì cả thư thành quảng cáo | Chưa ký |
| 9 | course_new | Bản tin | Nghiêng về có | Giới thiệu sản phẩm mới, một phần nội dung sau cổng Pro. Đề xuất chuyển sang Ưu đãi nếu khoá mới có phần trả phí | Chưa ký |
| 10 | community_weekly | Bản tin | Không | Lịch sinh hoạt của cộng đồng họ đã chọn làm nhà | Chưa ký |
| 11 | unit_story | Bản tin | Không | Nội dung giáo dục, dẫn tới trang công khai; lời giải không đi vào thư. Không được nhắc Pro hay giá | Chưa ký |
| 12 | book_chapter | Bản tin | Ranh giới | Phần đầu chương 1 đọc được ngay, phần sau cần Pro: thư không được hứa "đọc trọn chương", và không được chào Pro | Chưa ký |
| 13 | playbook_pick | Bản tin | Không | Bài công khai, không cần đăng nhập, không trả phí | Chưa ký |
| 14 | jtbd_case | Bản tin | Không | Chỉ bộ miễn phí, mở trọn ở cả hai nơi | Chưa ký |
| 15 | journey_day | Bản tin | Không, nếu không thư nào chào bán | Hướng dẫn dùng nền tảng mà người nhận đã đăng ký và đã chọn cộng đồng. Thư 22 của bản PR ("Pro: mời thử") đã bỏ: người có Pro nhận thư dịch vụ kể thứ Pro của họ mở thêm, người chưa có Pro nhận một câu lệnh AI dùng được ngay, không giá, không mời mua. Sửa một thư mà thêm Pro, giá hay học phí cho người chưa mua là cả loại thành quảng cáo (cần đồng ý + [QC]). Cần ký riêng vì đây là loại gửi dày nhất danh mục, gần "thư hàng loạt" nhất | Chưa ký |
| 16 | reconnect | Giữ liên lạc | Không, nếu chỉ hỏi ý | Thư chỉ hỏi một câu, nêu kênh, nội dung, nhịp tối đa, không giới thiệu sản phẩm. Vẫn là thư không được yêu cầu gửi cho người chưa đồng ý (cơ quan bảo vệ dữ liệu Anh từng phạt Flybe và Honda năm 2017 vì đúng loại thư "bạn còn muốn nghe từ chúng tôi không"), nên cần ký riêng | Chưa ký |
| 17 | win_back | Giữ liên lạc | Nghiêng về có | Kể "điều mới" của nền tảng cho người lâu không ghé. Mã đã lọc mục nhắc Pro, giá, học phí; nếu ký "có" thì chuyển sang Ưu đãi (cần đồng ý + [QC]) | Chưa ký |
| 18 | pro_offer | Ưu đãi | Có | Chào bán gói Pro. Đã có: đồng ý trước có phiên bản câu chữ, [QC] gắn ở cổng gửi, tối đa 1 thư/tháng mỗi cộng đồng. Còn thiếu: thông tin người quảng cáo (tên pháp nhân, địa chỉ, liên hệ) đặt ngay trước link từ chối | Chưa ký |
Loại nào người soạn ký "là quảng cáo" mà vẫn muốn gửi thì đổi cls của nó trong KIND_META sang uu_dai (một PR): cổng gửi tự gắn [QC] và bộ chấm tự đòi đồng ý, không mẫu thư nào phải sửa.
14. Danh sách kiểm trước khi bật live
Chuyển mode = live là một cú bấm, nhưng mỗi dòng dưới đây là một thứ mà hệ không tự thấy được nếu thiếu: thư vẫn đi, sổ gửi vẫn xanh, chỉ có kết quả là sai. Làm đủ rồi mới bấm.
| # | Việc | Vì sao | Xong khi |
|---|---|---|---|
| 1 | Cloudflare xác nhận bằng văn bản được gửi thư không giao dịch qua Email Service | FAQ nói chỉ giao dịch; một lần cưỡng chế rơi xuống cả tài khoản, kéo theo mã đăng nhập, biên nhận và nemo12.com | Có email/ticket trả lời, dán link vào Q1. Chưa có thì giữ live_classes = quan_he |
| 2 | Nhập hạn mức gửi/ngày của tài khoản vào "Hạn mức Cloudflare" (daily_quota) | Không biết hạn mức thì không biết trần có vượt không; vượt là mã đăng nhập hỏng tới 07:00 hôm sau | Admin chấp nhận chuyển live (API từ chối khi còn 0) |
| 3 | Nếu dùng tin.conan.school: bật Email Sending cho miền con, rồi mới đổi sender_domain | Đổi trước khi bật là mọi lượt gửi hỏng E_SENDER_* và loại đó tự bị giữ | wrangler email sending list thấy miền con; bộ mẫu gửi từ một alias @tin.conan.school tới được hai hộp duyệt |
| 4 | Tạo alias trong Google Workspace cho hộp hello@, hoặc chấp nhận giữ Reply-To hello@ | Người bấm "Trả lời" một alias chưa tạo thì thư dội 550 | Đã chọn một trong hai, ghi vào Q4 |
| 5 | [email protected] nhận được thư từ ngoài | Mọi Reply-To trỏ về đó; hộp không nhận được thì người muốn nói "đừng gửi nữa" bấm Báo spam | Gửi thử từ một Gmail cá nhân, thư tới hello@ |
| 6 | SPF ở gốc cho Google Workspace, bật DKIM của Workspace, thêm rua vào DMARC | DMARC đang p=reject mà gốc không có SPF cho Google: câu trả lời từ hello@ bị chính DMARC của mình từ chối; không có rua thì không thấy ai đang giả danh | dig thấy SPF có include:_spf.google.com, có google._domainkey, DMARC có rua= |
| 7 | Đăng ký Google Postmaster Tools cho conan.school (và tin.conan.school nếu dùng) | Báo spam và uy tín miền chỉ đọc được ở đó; hệ không tự thấy | Postmaster hiện miền đã xác minh |
| 8 | Cập nhật Chính sách quyền riêng tư: nêu các mục đích gửi thư (nhắc học, sự kiện và buổi học, bản tin, bài đọc, ưu đãi) và tần suất tối đa (1 thư gắn kết/ngày, ưu đãi tối đa 1 thư/tháng) | Luật BVDLCN 91/2025 đòi đồng ý theo mục đích; chính sách 04/2026 chỉ nêu nhắc bài tập và lịch học trực tiếp | Bản mới đã đăng ở www |
| 9 | Xác minh [email protected] làm địa chỉ đích ở Cloudflare | Hộp thư duyệt thứ hai nhận thư mẫu và bảng kế hoạch mỗi ngày | Cloudflare hiện địa chỉ đã xác minh |
| 10 | Người soạn ký bảng phân loại pháp lý (mục 13) cho mọi loại thuộc nhóm sắp bật | Nhóm nào chứa loại "là quảng cáo" thì phải chuyển loại đó sang Ưu đãi trước | Cột "Người soạn ký duyệt" không còn "Chưa ký" ở các nhóm sắp bật |
| 11 | Đọc bảng kế hoạch và thư mẫu ở preview ít nhất một ngày trọn | Thư mẫu là chỗ duy nhất thấy hệ quả trước khi nó xảy ra | Không loại nào có preview_error, không generator nào lỗi, không thư mẫu nào khiến người soạn muốn giữ lại |
15. Giai đoạn sau
Nhóm đối chứng 5% đo hiệu quả thật; sự kiện giao thư của Cloudflare (bounced/complained) qua Queue, để "ngày khoẻ" thấy được báo spam và bật lại cứng; trang tuỳ chọn trong tokyo; xem đủ 30 thư của journey_day ở admin và trong bộ mẫu (đợt này người duyệt chỉ thấy một thư chuỗi mỗi ngày); thư từ người sáng lập (nội dung do người soạn viết); video khi thư viện công khai; chọn giờ gửi theo từng người khi đủ dữ liệu không lệch giờ 08:00.