Files
rootnode-academy/docs/REGISTRATION_AND_GUARDIAN_COMMUNICATIONS.md
T
ced4568andClaude Sonnet 5 788fb197e0 docs(phase8): document enrollment operations
New: PHASE8_ENROLLMENT_OPERATIONS.md (phase summary), WINTER_BREAK_CAMP_OPERATIONS.md
(the real operational walkthrough), REGISTRATION_AND_GUARDIAN_COMMUNICATIONS.md,
OFFERING_ADMIN_GUIDE.md.

Updated: ROOTNODE_ACADEMY_BUSINESS_AND_PRODUCT_ARCHITECTURE.md (BUILT NOW/PHASE 8 BUILT
table), OFFERING_AND_REGISTRATION_MODEL.md (both ordering bugs, launch readiness,
registration enforcement), STUDENT_GUARDIAN_DATA_MODEL.md (guardian communications now
wired), WEBSITE_ENROLLMENT_INTEGRATION.md (the real, live registration contract with
worked examples, replacing the old speculative future-flow section), platform/README.md
(test list).

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-26 23:59:42 -05:00

63 lines
3.9 KiB
Markdown

# 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.