The DayPlan at the Heart of DormWay's Intelligence

At 5 AM every morning, while most college students are still asleep, DormWay generates a personalized intelligence artifact for each user. We call it DayPlan. It's not a to-do list. It's not a calendar clone. It's a daily brief that surfaces what actually matters: what's due today, what's fixed on your schedule, and where you're about to collide with yourself.
This post walks through how we built it, the technical decisions we made, and the problems we had to solve along the way.
The Problem With Existing Solutions
Students don't need another app that shows them their calendar. They already have Canvas. They already have Google Calendar. They already have Notion templates they abandoned in week two.
What they need is something that understands context. Something that knows the difference between a class that happens every Tuesday and an exam that happens once. Something that can look at five syllabi, a Canvas sync, and a calendar feed, then tell you: "You have chemistry at 9, two assignments due tonight, and you're double-booked at 2 PM."
That's what DayPlan does. Every day. Automatically.
The Architecture
DayPlan is generated by a Temporal workflow that runs continuously for each active student. Every five minutes, the workflow checks whether it's 5 AM in the student's timezone. If it is, and we haven't generated today's plan yet, we kick off generation.
StudentWatcher (Temporal Workflow)
↓ Every 5 minutes
↓ Check: is it 5 AM user time?
↓ Check: do we have today's plan?
↓ If no → generateMorningPlan()The timezone handling is critical. We don't generate at 5 AM UTC and hope for the best. We generate at 5 AM in your timezone. A student in LA gets their plan at 5 AM Pacific. A student in New York gets theirs at 5 AM Eastern. Same system, different clocks.
const userTimeString = new Date().toLocaleString('en-US', { timeZone: timezone });
const userTime = new Date(userTimeString);
const userHour = userTime.getHours();
if (userHour === 5 && userMinute < 5 && needsNewDayPlan) {
await generateMorningPlan(studentId, timezone);
}Data Aggregation
Before we can generate a plan, we need to gather data. DayPlan pulls from multiple sources in parallel:
- Canvas assignments via LMS sync
- Syllabus data we've already parsed
- Calendar events from Google/Outlook integrations
- Time blocks (classes, recurring meetings)
- Weather data for the student's location
- Campus context (events, alerts, logistics)
- Semester insights (peak weeks, milestones)
- Yesterday's deviations (more on this later)
All of this gets assembled into a raw input object that feeds the generation step.
The LLM Layer
Here's where it gets interesting. DayPlan is generated by an LLM (Gemini 2.5 Flash Lite), but we've built significant guardrails around it.
The core problem with using LLMs for scheduling is hallucination. Feed a model a student's schedule and ask it to plan their day, and it will happily invent classes that don't exist, meetings that were never scheduled, and study sessions it thinks would be helpful. That's useless at best and harmful at worst.
Our approach: strict anti-hallucination rules baked into the prompt.
## CRITICAL INSTRUCTIONS - ANTI-HALLUCINATION RULES
1. ONLY use events explicitly provided below
2. DO NOT invent, assume, or generate ANY events not listed
3. If no classes are listed, DO NOT add any classes
4. If no meetings are listed, DO NOT add any meetings
5. If no assignments/Canvas data, DO NOT create generic "study blocks"
6. Every event you create MUST be justified by the provided dataWe reinforce these rules multiple times throughout the prompt. Header rules, section markers ("CLASSES: NONE SCHEDULED - DO NOT ADD ANY"), output constraints. The LLM is explicitly forbidden from being creative with the student's schedule.
V2: Priority-First Philosophy
We shipped the first version of DayPlan (V1) and quickly realized we'd built the wrong thing. V1 tried to be a micromanaging scheduler—here's your 8 AM block, here's your 9 AM block, here's when you should eat lunch. Students hated it. It felt like the app was telling them how to live their lives.
V2 flipped the model. Instead of a timeline, we lead with priorities:
┌─────────────────────────────┬─────────────────────────────────────┐
│ V1 (Deprecated) │ V2 (Current) │
├─────────────────────────────┼─────────────────────────────────────┤
│ Timeline of events │ Due Today as the hero section │
│ AI suggestions mixed in │ Hard commitments clearly separated │
│ Schedule-first │ Priority-first │
└─────────────────────────────┴─────────────────────────────────────┘
The V2 output structure:
interface DayPlan {
dueToday: AssignmentItem[]; // Hero field - what's actually due
hardCommitments: HardCommitment[]; // Classes, exams, fixed meetings only
conflicts: ConflictAlert[]; // Proactively surfaced problems
suggestedBlocks: SuggestedBlock[]; // Clearly labeled as optional
nearFuture: AssignmentItem[]; // Next 2-3 days for awareness
summary: DayPlanSummary; // Natural language overview
}The key insight: students don't need us to schedule their day. They need us to surface what matters and get out of the way.
Conflict Detection
One of the highest-value features is conflict detection. The system automatically identifies:
- Overlaps: Two events at the same time
- Tight gaps: 10-minute window between a class ending and an exam starting across campus
- Deadline collisions: Assignment due during a class you can't miss
type ConflictAlert = {
type: 'overlap' | 'tight_gap' | 'deadline_during_class';
affectedItems: string[];
severity: 'warning' | 'critical';
};These get surfaced proactively in the plan. The student sees: "Heads up—you have an essay due at 3 PM but you're in Physics 101 from 2-3:30. You might want to submit early."
Deviation Learning
This is probably the most interesting technical feature. DayPlan doesn't just generate plans—it learns from when students deviate from them.
Throughout the day, the StudentWatcher workflow monitors for anomalies:
- Location anomalies: GPS shows you're 50 miles from campus during what should be a class
- Activity mismatches: Context signals suggest you're sleeping when the plan expected you in lecture
- Calendar conflicts: A new event appeared that overlaps with the plan
These deviations are stored and accumulated. The next morning, when we generate the new DayPlan, we inject yesterday's deviations into the prompt:
## Learning from Previous Patterns
Recent deviations to account for:
- schedule_change: Student marked as sleeping during scheduled 9 AM class
- location_anomaly: Student was 30 miles from campus during afternoon
Adjust today's plan based on these patterns.The LLM can then factor in that maybe this student doesn't actually go to their 9 AM, or that they're commuting from off-campus and need more buffer time.
Academic Break Mode
College schedules aren't uniform. Students have winter break, spring break, summer. When we detect a student is on break (via term data from the university), we flip DayPlan into break mode:
## 🎄 ACADEMIC BREAK MODE - CRITICAL
The student is currently on Winter Break. This changes EVERYTHING:
1. DO NOT recommend studying, homework, assignments
2. DO NOT create study blocks or prep sessions
3. DO NOT mention upcoming deadlines
4. Focus on RELAXATION, REST, and ENJOYMENT
5. Keep the plan minimal - breaks are for unwindingThis prevents the app from nagging students to study during Christmas break. Sounds obvious, but most scheduling apps don't understand academic calendars at all.
The Canvas ICS Timezone Bug
One of the gnarlier problems we had to solve: Canvas exports assignment due dates in ICS format using DATE-only values (no time component). Per RFC 5545, these get interpreted as UTC midnight.
For students in negative UTC timezones (most of the US), this means an assignment "due December 15" shows up as due December 14 at 4 PM Pacific.
Canvas ICS: DTSTART;VALUE=DATE:20241215
→ Parsed as: 2024-12-15T00:00:00Z
→ Displayed in PST: Dec 14, 4:00 PM ❌
The fix: detect all-day events during parsing and store them as 23:59:59 on the specified date. "Due December 15" becomes "due by end of day December 15" which displays correctly in any timezone.
Cold Start Honesty
When a new student signs up, we have no data. No schedule, no Canvas sync, no syllabi. The tempting approach: generate a fake "exploratory" schedule with placeholder events like "Morning routine" and "Campus Discovery Time."
We tried that. It felt like lying.
V2 approach: be honest. If we don't have data, say so.
if (isInsufficientData) {
return {
generationStatus: 'cold_start',
summary: {
oneLiner: "Upload your schedule to see your classes and free time",
dayLoad: 'unknown'
},
dueToday: [],
hardCommitments: [],
// ... empty but defined fields
};
}The UI shows a helpful empty state that guides the student to upload their first syllabus or connect Canvas. No fake events pretending we know things we don't.
Notifications as a "Music Score"
DayPlan includes an actions array—scheduled notifications that fire throughout the day. We think of it as the "music score" of the student's day: class reminders 15 minutes before, assignment reminders 2 hours before due time, evening briefing at 8 PM.
type ScheduledAction = {
actionTime: string; // ISO timestamp
actionType: 'class_reminder' | 'assignment_reminder' | 'evening_briefing';
title: string;
message: string; // Personalized based on communication style preference
executed: boolean;
};The StudentWatcher workflow checks every 5 minutes whether any actions are due, respects quiet hours preferences, and delivers via Customer.io (push, email, or SMS based on user settings).
Every Surface is the Product
Here's something we care deeply about: nothing DormWay sends is generic.
Most apps treat email and push notifications as afterthoughts. "Don't forget to check your tasks!" with a stock subject line. Students learn to ignore them. We took the opposite approach: every notification, every email, every subject line is generated specifically for that student based on their actual day.
The morning briefing email doesn't say "Here's your daily schedule." It says "Chem exam at 10, essay due tonight" in the subject line. The body tells you exactly what's happening today, what's due, and what conflicts we detected. It's written by the same LLM that generates DayPlan, with full context about your courses, your schedule, and your preferences.
// Part of the DayPlan generation prompt
notificationContent: {
morningEmail: {
subject: string; // Generated: "2 classes, calc homework due 11:59pm"
preview: string; // Generated: "Plus: office hours conflict with your study group"
body: EmailContent; // Full personalized briefing
}
}The result: our morning email open rates are near 70%.
That's not a vanity metric. It validates a core product principle: you should be able to get high value from DormWay without ever opening the app. Email is a product surface. Push notifications are a product surface. If a student just reads their morning briefing and knows their day, we've done our job.
This also changes how we think about the app itself. The iOS app doesn't need to be "sticky" in the engagement-farming sense. We're not trying to maximize time-in-app. We're trying to minimize the time you spend wondering what's due. Sometimes that means you check the app. Sometimes that means you just read the email.
What We're Building Next
DayPlan V3 is on the roadmap with enhancements focused on multi-day awareness:
- Weekly planning view: Sunday evening digest of the week ahead
- Cross-day conflict detection: "You have 4 things due Thursday but Wednesday is light"
- Schedule gap detection: "You have 2 hours free before your next class—want to work on CS homework?"
- Smart digest timing: Don't send the morning briefing if the student is currently in an exam
The Takeaway
DayPlan works because it does a few things well:
- Aggregates data students already have across fragmented systems
- Applies intelligence without inventing things that aren't there
- Leads with priorities rather than micromanaging time
- Learns from deviations to improve future plans
- Respects context (timezone, break mode, preferences)
- Treats every surface as product—email and push deliver real value, not just reminders
It's not magic. It's careful engineering applied to a real problem that 20 million college students deal with every semester.
DormWay is hiring engineers who want to build systems like this. If this kind of problem interests you, reach out.
About Ethan
Co-Founder & CTO
Ethan is the tech brains behind the scene. A long time music executive, Ethan spends his days mostly juggling various AI bots and infrastructure.