Documentation Index
Fetch the complete documentation index at: https://mintlify.com/vruizz22/innova-ai-engine/llms.txt
Use this file to discover all available pages before exploring further.
hourlyAlerts is the platform’s real-time risk-detection layer. It runs every hour, pulling the current state of student mastery and guide-submission grading data, and evaluates six detectors against configurable thresholds. Each detector produces AlertCandidate objects that are deduplicated before insertion into the teacher_alerts table — so a teacher is never shown the same alert twice in a day for the same entity. The worker covers two families of alerts: mastery-based signals (at-risk students, learning drops, off-track units, and widespread topic struggles) derived from BKT p_known values, and guide-based signals (grading completion and common submission errors) derived from the v9 guides pipeline. Running hourly rather than nightly means teachers receive actionable notifications within 60 minutes of the underlying data changing.
Trigger and configuration
| Property | Value |
|---|---|
| Trigger | EventBridge scheduled rule |
| Schedule | cron(0 * * * ? *) — every hour, on the hour |
| Timeout | 900 s |
| Memory | 1024 MB |
| Handler | src.pipeline.hourly_alerts.handler |
| Event payload | (none read — scheduled invocation) |
Return payload
The handler returns a run-level summary:candidates— total alerts produced by all detectors before deduplicationinserted— alerts that were new (not already present) and written to the databaseby_type— breakdown of inserted alerts byalert_type
Alert types
The six detectors are run in sequence insiderun_hourly_alerts. All operate on data fetched once at the start of the invocation: mastery rows, course sizes, guide progress rows, and guide error rows.
1. AT_RISK_STUDENT
A student is flagged as at-risk when they have ≥ ALERT_AT_RISK_MIN_TOPICS topics with p_known < ALERT_AT_RISK_PKNOWN_FLOOR within a single course.
Group mastery rows by (course, teacher, student)
All active enrolment rows for the course are grouped per student.
Count weak topics
Topics where
p_known < pknown_floor are counted. If count < min_topics, the student is skipped._count_severity(len(weak), min_topics):
HIGH— ≥2 × min_topicsweak topicsMED— ≥min_topicsweak topicsLOW— below threshold (never emitted)
2. STUDENT_DROP
A student is flagged when their 7-day mastery trend on at least one topic falls at or below ALERT_STUDENT_DROP_TREND (a negative value).
Find dropped topics per student
Topics where
trend_7d <= drop_threshold are collected. If none qualify, the student is skipped._drop_severity(worst_trend, threshold):
HIGH—worst_trend <= threshold × 2(double the configured drop)MED—worst_trend <= thresholdLOW— below threshold (never emitted)
3. UNIT_OFF_TRACK
A unit is flagged when the course-average p_known across all of the unit’s topics is below ALERT_UNIT_OFF_TRACK_FLOOR.
Group p_known values by (course, teacher, unit)
All mastery rows are grouped by unit within each course.
Compute average p_known
The mean of all
p_known values in the group is computed. If avg >= pknown_floor, the unit is skipped._deficit_severity(avg, pknown_floor):
HIGH— deficit>= 0.2(i.e.,avg <= floor − 0.2)MED— deficit>= 0.1LOW— smaller deficit
4. COMMON_ERROR_IN_TOPIC
A topic is flagged when the ratio of students below the mastery floor in that topic reaches or exceeds ALERT_TOPIC_STRUGGLE_RATIO.
Compute struggle ratio
struggling / course_size, where struggling is the count of students with p_known < pknown_floor. Skipped if course_size == 0 or ratio < course_ratio._severity_from_ratio(ratio):
HIGH— ratio ≥ 0.66MED— ratio ≥ 0.40LOW— ratio below 0.40 (never emitted due to threshold guard)
5. GUIDE_GRADING_COMPLETE
An informational alert is emitted when ≥ ALERT_GUIDE_COMPLETE_RATIO of enrolled students have a graded submission for a published guide.
For each guide progress row
graded_students / course_size is computed. If ratio < complete_ratio, the guide is skipped.LOW — this is an informational notification, not a warning.
6. GUIDE_COMMON_ERROR
A guide question is flagged when the same definitive error_code appears for ≥ ALERT_GUIDE_COMMON_ERROR_RATIO of enrolled students. Catalog-independent sentinel codes (defined in SPECIAL_ERROR_TYPES) are excluded.
For each guide error row
n_students / course_size is computed. Rows where error_code is a special sentinel are skipped._severity_from_ratio as COMMON_ERROR_IN_TOPIC.
AlertCandidate schema
UUID of the teacher who owns the course. Used to route the alert to the correct dashboard.
UUID of the course the alert belongs to.
One of
AT_RISK_STUDENT, STUDENT_DROP, UNIT_OFF_TRACK, COMMON_ERROR_IN_TOPIC, GUIDE_GRADING_COMPLETE, or GUIDE_COMMON_ERROR.One of
LOW, MED, or HIGH. Computed per-detector as described above. Defaults to MED if not set.Entity identity used for daily deduplication (
(teacher, type, dedup_ref, day)). Prevents the same alert from being inserted more than once per day for the same entity.Heterogeneous JSON written to
teacher_alerts.payload. Contents are specific to each alert_type — see the per-detector descriptions above.Present for topic-level alerts (
COMMON_ERROR_IN_TOPIC). null for all other types.Present for student-level alerts (
AT_RISK_STUDENT, STUDENT_DROP). null for all other types.Severity levels
| Level | Meaning |
|---|---|
LOW | Informational — no immediate action required (e.g., guide grading complete) |
MED | Attention warranted — a pattern is developing that a teacher should review |
HIGH | Urgent — a significant proportion of students or a steep drop requires prompt intervention |
detectors.py: _severity_from_ratio, _deficit_severity, _drop_severity, and _count_severity. Each uses simple linear cut-offs against the configured thresholds.
Deduplication
Before inserting a candidate, the worker callsrepo.insert_alert_if_absent(candidate), which checks whether a teacher_alerts row already exists for the same (teacher_id, alert_type, dedup_ref, day). If one does, the insert is skipped and inserted is not incremented. This ensures that running the worker 24 times per day does not flood the teacher’s dashboard with repeated alerts.
Configurable thresholds
All detection thresholds are read from environment variables viapydantic-settings (src/shared/settings.py). Override them in .env or as Lambda environment variables without redeploying code.
| Environment variable | Default | Description |
|---|---|---|
ALERT_AT_RISK_PKNOWN_FLOOR | 0.4 | Mastery floor below which a topic is considered weak. Used by AT_RISK_STUDENT, COMMON_ERROR_IN_TOPIC, and UNIT_OFF_TRACK. |
ALERT_AT_RISK_MIN_TOPICS | 3 | Minimum number of weak topics a student must have to trigger AT_RISK_STUDENT. |
ALERT_TOPIC_STRUGGLE_RATIO | 0.5 | Minimum fraction of enrolled students below the mastery floor to trigger COMMON_ERROR_IN_TOPIC. |
ALERT_STUDENT_DROP_TREND | -0.15 | Maximum (most negative) 7-day mastery trend before STUDENT_DROP fires. |
ALERT_UNIT_OFF_TRACK_FLOOR | 0.4 | Course-average mastery floor below which UNIT_OFF_TRACK fires for a unit. |
ALERT_GUIDE_COMPLETE_RATIO | 0.9 | Minimum fraction of enrolled students with graded submissions to trigger GUIDE_GRADING_COMPLETE. |
ALERT_GUIDE_COMMON_ERROR_RATIO | 0.3 | Minimum fraction of enrolled students sharing the same error code to trigger GUIDE_COMMON_ERROR. |
Local invocation
DATABASE_URL is set in your .env file. The function requires student_skill_mastery, enrollments, items, skills, teacher_alerts, and the guide-related tables to be accessible.