# Registration & Guardian Communications Phase 8 §8-§11 — how registration-stage notifications extend the existing Phase 5C notification system. No second email subsystem was built: same `notification_events` table, same durable worker (`notification-worker.js`), same Google Workspace-backed provider adapter (`email/provider.js`), same masked-recipient admin API. ## What's new vs. Phase 5C `notification_events.student_id` is now nullable, and a `registration_id` column was added (a CHECK constraint guarantees every row still has a real subject) — because a registration-stage notification (Registration Received, Agreement Required) may need to go out before any student account exists at all, for a brand-new guardian-submitted registration. `notification_type`'s CHECK constraint was widened to include five new types. See `033_phase8_...sql` and `036_phase8_notification_types.sql`. **A real bug found and fixed during this phase's own testing**: the new notification types were added to `notifications.js`/`notification-worker.js` before the database CHECK constraint was widened to accept them — every registration-stage `enqueue` call was silently failing (caught by the deliberate "a notification failure never blocks the real action" try/catch) until this was caught and fixed. See `docs/PHASE8_ENROLLMENT_OPERATIONS.md`. ## The five new templates 1. **Registration Received** — queued the moment a registration is created (website or admin). 2. **Registration Confirmed** — queued when a registration moves to `confirmed`. 3. **Registration Needs Action** — available for an admin/future-flow to queue with a reason when a registration needs something before it can proceed (not automatically triggered by any single lifecycle transition this phase — a deliberate choice, since "needs action" covers many different real reasons an admin should state explicitly). 4. **Agreement Required** — queued once per missing required agreement when a registration enters `agreements_pending`. 5. **Camp/Offering Operational Update** — a deliberately generic operational notice (e.g. "camp starts Monday, here's what to bring"), NOT a marketing template — no promotional content, no discount codes, no unrelated announcements. Available for an admin to trigger manually; nothing queues it automatically. All five are HTML + plain-text, RootNode-branded, no tracking pixels, no external images, no passwords or sensitive data — same `templates.js` conventions Phase 5C established. ## Recipient resolution — extended, never overwritten `resolveRecipient()`'s existing order (student's own email → the enrollment's `metadata.contact_email`) now has a third, final fallback: a real, communications-enabled primary guardian's email (`student_guardians`/`guardians`). This is purely additive — a student who already resolves correctly today is completely unaffected, since the new fallback is only ever reached when the first two return nothing. `enrollmentContactEmail` (Phase 5C, unchanged) and this new guardian fallback deliberately coexist: `contact_email` remains a per-enrollment override for the live website integration's existing contract, while the guardian relationship is the durable, cross-enrollment identity — prefer the guardian model going forward, but the transitional field is never broken. For registration-stage notifications (no student yet, possibly), a separate `resolveRegistrationRecipient()` tries: the registration's own student (if already converted) → its linked guardian → the registrant's own supplied email. ## What was deliberately NOT built No bulk/marketing email composer, no free-form admin-authored message sent to an arbitrary recipient — every notification is one of the fixed templates above, triggered by a real lifecycle event or an explicit, scoped admin action (never a raw "compose email" form). No SMS. No third notification channel beyond email.