Getting students in
A teacher creates a class and generates an invite code for it. Each code carries two limits: how many times it can be used (usesRemaining) and, optionally, when it expires. A student registers with the code and is placed into that class automatically — no manual roster entry, no separate approval step. Codes can be revoked, and a class can have more than one active code at a time (useful for re-issuing one that’s run out of uses without invalidating the whole class’s access).
What the roster shows
Open a class and the roster lists every student with their practice count and average score, computed live from their attempt history — not a snapshot that goes stale. “Last active” comes from the same source. None of this is manually entered by the teacher; it’s a read over data students generate just by practicing.The class-wide weak-term board
Below the roster sits a ranked list of the terms the whole class keeps missing — aggregated the same way an individual student’s weak-term list is (see Practice loop), just pooled across every student in the class instead of one. This is the part of the dashboard actually meant to change what a teacher does next: if a dozen students in the same class keep missing the same official rendering, that’s not noise, it’s next week’s five-minute review.The scoring pipeline is written once and read twice: a single sight-interpretation attempt writes one row, and that row feeds both the student’s own progress curve and the class-wide board a teacher sees. There is no separate “classroom analytics” computation running on a different schedule with its own chance to drift out of sync.