HandbookAcademics28

Academic recovery.

Every term leaves a few students behind — a failed subject, a run of absences, a near-miss average. Academic recovery is the safety net under the academic spine: a catch-up window the school opens after results, a rule that decides who is in it, a parent who consents, a score that — if the school says so — replaces the one that failed. This handbook walks the recovery loop end to end, and shows where resits and exam-prep sit beside it.

  • 2surfaces (admin + portal)
  • 5eligibility triggers
  • 4grade-outcome rules
  • 7enrolment statuses

Prologue

What recovery actually covers#

The marketing line for this module reads "catch-up, remedial, resits, exam-prep." That is the family of ideas. The part this handbook documents in depth — and the part with its own tables, its own admin screen, and its own grade-writeback engine — is academic recovery: the catch-up window a school opens for students who did not pass the term. It is built on two tables, academic_recovery_configs and academic_recovery_enrolments, plus the shared holiday-class window primitive.

The two neighbours are real but separate, and we are honest about that here. Resits are a distinct module with their own rules engine, their own pages (/dashboard/grades/resits and /portal/resits) and their own report-card override. Exam-prep is not a standalone feature at all — it is a purpose tag on a holiday-class window (purpose = 'exam_prep'), scheduled through the holiday-classes surface at /dashboard/timetable/holiday-classes. The last chapter of this book maps those neighbours so you know which screen to open.

Recovery is the term's second chance — granted by rule, paid for or waived, consented to, and recorded against the grade that failed.

Chapter one

How a student lands in recovery#

Recovery starts where the year ends. When the registrar runs the end-of-year class rollover at /dashboard/grades/promotion/run, the promotion engine writes each student a promotion status — promoted, retained, or conditional. The rollover then makes a best-effort call to the database function auto_route_retained_to_recovery: for every retained or conditional student it works out the subjects they actually failed (each subject scored below the school's pass mark), finds the published recovery window that covers their class, and writes them an enrolment row in status eligible — with the failed subject ids stored on enrollment_subjects and the term that triggered it stamped on triggered_by_term_id.

The routing is idempotent — re-running the rollover never creates a duplicate, because the enrolment table carries a UNIQUE (recovery_window_id, student_id) constraint. It is also wrapped so that a routing failure can never abort a rollover that has already committed promotions. The rollover's result JSON reports a recovery_routed count so the registrar can see how many students were picked up.

Designed product mock — the recovery enrolment list: students routed from the rollover, each with their failed subjects and status.

Chapter two

The rules that define eligibility#

Before the routing can know who qualifies, the school writes the rules. A recovery config — created at /dashboard/recovery — is a small policy object: a trigger, an optional threshold, and a rule for what a recovery score does to the grade. The admin page's first section is a form that saves one config; below it, every active config is listed as a row of badges.

What a config carries

  1. 1

    Trigger type

    Five values, exactly as the database CHECK enforces: subject_average_below (per-subject threshold — the common one), overall_average_below (whole-cohort threshold), failed_subject_count (an N-fails policy), attendance_below (recovery driven by absence), and manual_referral (the escape hatch for a teacher's judgement).

  2. 2

    Threshold

    A number that pairs with the trigger — e.g. a subject average ≤ 8 out of 20, or 3 failed subjects. Stored as trigger_threshold; the config row shows it as ≤ value.

  3. 3

    Outcome rule

    What a recovery score does to the term grade — four values: no_grade_impact (recovery is informational, pass/fail only — the default, on the principle that changing a grade by default is unfair), replaces_term_grade_if_pass, best_of_two, and average_with_term_grade.

  4. 4

    Mandatory + consent

    mandatory_for_eligible marks the window compulsory for anyone who qualifies; parent_consent_required (on by default) means a parent must say yes before the student is counted in.

  5. 5

    Fee + waiver

    A config can carry a fee_amount and currency, with fee_waiver_for_scholarship_students on by default so aided families are not charged for a second chance.

Chapter three

The recovery window#

A config decides who; a window decides when. Recovery windows are not a separate table — they reuse the same holiday_class_windows primitive that all holiday-class scheduling runs on, tagged with purpose = 'academic_recovery'. A window carries a name, a date range, the classes / sections / subjects it covers, an optional fee, a consent flag, and a published flag. It is created and published from the holiday-classes surface at /dashboard/timetable/holiday-classes.

The auto-router only enrols students into a window that is published and that scopes to their class. So the sequence at year-end is: publish the recovery window first, then run the rollover — the routing slots retained students straight into it.

Chapter four

Consent, attendance, and scoring#

The second section of the recovery admin page is the window's roster. The admin enters a window's id to load its enrolments; each row shows the student, their status badge, consent and fee-waived markers, and the recovery score once recorded. An enrolment moves through seven statuses: eligible → enrolled → attended → graded → final, with withdrew and no_show as the two off-ramps.

Two actions live on each row. Record consent stamps parent_consent_at for a student whose family has agreed — available to anyone with academic_recovery.manage or students.edit. Grade captures the recovery score and sets the status to final. The grade itself does not touch the report card yet — that is a deliberate second step, covered next.

Chapter five

When a recovered score replaces the grade#

The bridge between a recovery score and the report card is a single boolean: applied_to_term_grade. While it is false, the score is recorded but advisory. The moment it flips to true, a database trigger (trg_academic_recovery_apply_outcome) fires the function apply_recovery_outcome, which reads the config's outcome rule and writes the result back into the grade ledger.

It does this carefully. Rather than overwrite the original term score, it writes the recovery result against a synthetic, zero-weight RECOVERY exam type (created once per school by get_or_create_recovery_exam_type), so the recovered mark acts as a subject-level override without disturbing the normal CA-plus-exam weighting. For each failed subject it applies the rule — replace-if-pass, best-of-two, or average — resolves the new letter grade and GPA from the school's grade boundaries, and upserts a student_scores row. Under no grade impact it writes nothing and simply records the outcome for the audit trail.

There is a second path to the same trigger. When a recovery window is run as a graded holiday class and a student passes it, a bridge trigger on the holiday-class enrolment flips the matching recovery row to applied_to_term_grade = true automatically — closing the loop without a manual step.

Chapter six

What the student and parent see#

Recovery is not a back-office secret. Every student and parent has a read-only window onto it at /portal/recovery. A parent with more than one child picks a child; the page then lists that student's recovery enrolments. Each card shows the status, the recovery score, an "applied to term ✓" marker when the writeback has fired, the number of subjects under recovery, parent-consent and fee-waived notes, and any free-text note the school added.

The portal is strictly a consumer surface — enrolling, configuring, and grading all stay on the admin web. Row-level security narrows the read to the caller's own (or their child's) enrolments, so a family only ever sees their own.

Chapter seven

Recovery's two neighbours#

Resits are the formal re-sit of a paper, governed by per-program resit rules, auto-spawned when a term is compiled, billed (when run as a holiday resit) and overlaid back onto the report card through a resit override. They are a separate module with their own admin page at /dashboard/grades/resits and a family view at /portal/resits. If you are looking for "the student re-sits the exam," that is resits, not recovery.

Exam-prep has no page of its own. It is a holiday window with purpose = 'exam_prep' (and 'resit_prep' for resit revision), scheduled at /dashboard/timetable/holiday-classes. Think of it as recovery's lighter cousin: coaching time before the exam rather than a second chance after it.

The differentiators

What makes recovery elite#

Five things most systems skip

  1. 1

    Routing by the rollover, not by hand

    Retained and conditional students are routed into the right recovery window automatically when the year closes — failed subjects computed, term stamped, duplicates impossible. No one has to remember to enrol the strugglers.

  2. 2

    Grade changes are opt-in, not default

    The default outcome rule is no_grade_impact, on the explicit principle that silently changing a grade is unfair. A school chooses replace-if-pass, best-of-two, or average — per config — and the choice is auditable.

  3. 3

    Override without overwrite

    A recovered score is written against a synthetic zero-weight RECOVERY exam type, so it overrides the subject grade without disturbing the original CA-plus-exam scores. The first attempt is never erased.

  4. 4

    Two ways to close the loop

    Either an officer flips applied_to_term_grade by hand, or a student passes the recovery window as a graded holiday class and a bridge trigger flips it for you — both land on the same writeback function.

  5. 5

    The family sees the second chance

    Students and parents read their own recovery status, score, and 'applied to term' marker in the portal — recovery is a visible support, not a hidden remediation.

Honest roadmap

What comes next#

The recovery engine is complete and has been verified against the live database; the admin authoring surface is where the rough edges are. These items are planned.

Four honest gaps

  1. 1

    A window picker

    Today the admin roster loads by pasting a holiday-class window id into a text field. A proper picker that lists the school's published recovery windows is the obvious next step.

  2. 2

    A grading form

    Scoring a student currently uses a browser prompt. A real per-student grading form — ideally per subject, with the outcome-rule preview — replaces it.

  3. 3

    Hand-enrolment from the page

    The data layer can enrol a student into a window directly, but the admin page exposes no 'enrol' button — enrolment is auto-route and trigger-driven only. A manual add covers the manual_referral trigger.

  4. 4

    Real assessment, not a proxy

    When a recovery row is synthesised from a passed holiday class, the bridge currently uses the student's attendance rate as a stand-in score until a proper recovery assessment is captured. Real per-subject scoring is planned.

Where recovery connects

Tutorial

Do it step by step#

Open a recovery window and carry a retained student through it — from the rule that catches them to the grade that, if your school says so, replaces the one that failed.

  1. 1

    Write the recovery rule

    Open /dashboard/recovery and save a config: pick a trigger (e.g. subject average below), set the threshold, choose an outcome rule, and set whether parent consent is required. Leave the rule on no grade impact until your policy is agreed.

    The default outcome rule is deliberately the safe one — recovery shouldn't change a grade until the school has decided it should.

  2. 2

    Publish the window

    In /dashboard/timetable/holiday-classes, create a holiday-class window tagged for academic recovery, scope it to the classes and subjects it covers, and publish it. Publishing first is what lets the rollover route students into it.
  3. 3

    Run the rollover

    Close the year at /dashboard/grades/promotion/run. Retained and conditional students are routed into the published window in status eligible, with their failed subjects attached. The result reports a recovery-routed count.
  4. 4

    Record consent

    Back on /dashboard/recovery, load the window's roster and click Record consent for each family that has agreed — the row stamps the consent timestamp and surfaces a green marker.
  5. 5

    Score the recovery

    After the catch-up sessions, click Grade on each row to capture the recovery score. The status moves to final; the score is recorded but does not yet touch the report card.

    Grading sets the score; it does not change the grade. That separation is intentional — the writeback is a second, explicit step.

  6. 6

    Apply it to the term

    When you are ready, flip the enrolment's applied to term flag. The writeback trigger applies your outcome rule, resolves the new letter grade, and updates the subject on the report card — leaving the original score intact in the audit. The family sees it at /portal/recovery.

The term's near-miss has a documented second chance — routed by rule, consented to, scored, and — only if your school decided so — written back over the grade that failed.