- integration-routes.js: accepts student.contact_email, calls notifyForEnrollment on a
genuinely new enrollment only (never on an idempotent replay — inherits Phase 5B's proven
enrollment-level idempotency, no separate notification dedup needed).
- admin-routes.js: create-student/convert/reset-password now enqueue the corresponding
notification using the SAME token already minted synchronously (never mints twice, which
would silently invalidate the token just handed back in the response). Fixed a real bug:
request-password-reset would mint a reset token for a never-activated student, which makes
no sense and would confuse them — now falls through to the generic anti-enumeration response.
- admin-programs-routes.js: manual enrollment triggers the same notification decision as the
website path.
- student-auth-routes.js: /activate records a metric; /change-password and /reset-password
both queue a password_changed security notice; /request-password-reset now actually delivers
email (production still never returns the token — unchanged anti-enumeration/isProduction
gating, just now with a real send behind it).
- rbac.js: requireStudentRelationshipAccess/checkStudentRelationshipAccess — the notification
visibility rule (cohort-shared or an enrollment they created), reusable beyond just
enrollments.
- server.js: wires notification-routes.js and starts the notification worker in main().
- docker-compose.dev.yml: PUBLIC_APP_ORIGIN, EMAIL_FROM_NAME, and a parametrized COOKIE_SECURE
(was hard-commented-out; needed so e2e-phase5c.js can actually prove production gating).
- migrations/017: email, login_identifier, password_hash, last_login_at, credential_token_*
on students. Fully additive, both nullable/independent, no destructive change.
- students-auth.js: shared credential/token lifecycle (issue/consume/find), reusing
instructors' exact scrypt password.js verbatim.
- student-auth-routes.js: /api/student/login, /activate, /change-password,
/request-password-reset (anti-enumeration, dev-only token echo), /reset-password, /profile.
- metrics.js: hand-rolled /metrics Prometheus exporter, login success/failure counters, no
student/instructor ID ever used as a label value.
- migrations/018: classroom_sessions/participants/events/announcements/help_requests, with a
partial unique index enforcing at most one live classroom per cohort at the DB level.