Implementation PR Roadmap
This chapter breaks down the Registration system into small, reviewable pull requests. It complements the Implementation Plan with concrete PR scopes, dependencies, and acceptance criteria.
Guiding principles:
- Keep PRs tight and shippable behind feature flags.
- Prefer vertical slices that produce visible value.
- Add tests and docs incrementally with each PR.
Plan ↔ PR crosswalk (Registration workstream):
- Step 2 — Foundations (schema/backend): PR-2.x
- Step 3 — FCFS mode (admin + student): PR-3.x
- Step 4 — Preference-based mode (student + allocation): PR-4.x
- Step 5 — Roster maintenance: PR-5.x
- Scope: AR models
Registration::Campaign,Item,UserRegistration,Policyand additive migrations. - Migrations:
20251028000000_create_registration_campaigns.rb20251028000001_create_registration_items.rb20251028000002_create_registration_user_registrations.rb20251028000003_create_registration_policies.rb
- Refs: Models — Campaign, Item, UserRegistration, Policy
- Acceptance: Migrations run cleanly; models have correct associations and validations; no existing tables altered.
- Scope:
Registration::PolicyEnginewith two policy kinds. - Implementation:
Registration::PolicyEngine#evaluate_policies_for,Registration::Policy#evaluateforinstitutional_emailandprerequisite_campaignkinds. - Test doubles: Tests use doubles for checking roster membership in prerequisite campaigns.
- Refs: PolicyEngine, Policy#evaluate
- Acceptance: Policy engine evaluates ordered policies with short-circuit; tests pass with doubled roster data;
student_performancepolicy kind deferred to Step 11.
- Scope: Include
Registration::CampaignableinLectureandRegistration::RegisterableinTutorial. - Implementation:
Campaignable:has_many :registration_campaigns, as: :campaignableRegisterable:capacity,allocated_user_ids(raises NotImplementedError),materialize_allocation!(raises NotImplementedError)
- Refs: Campaignable, Registerable
- Acceptance: Tutorial includes Registerable; methods raise NotImplementedError when called; no functional changes to existing semester.
- Scope: Include
Registration::CampaignableinSeminarandRegistration::RegisterableinTalk. - Same pattern as PR-2.3 but for seminars.
- Refs: Same concerns, different models.
- Acceptance: Same as PR-2.3 for seminar context.
- Scope: Teacher/editor UI for campaign lifecycle (draft → open → closed → processing → completed).
- Controllers:
Registration::CampaignsController(new/create/edit/update/show/destroy). - Actions:
open(validates policies, updates status to :open),close(background job triggers status → :closed),reopen(reverts to :open if allocation not started). - Freezing: Campaign-level attributes freeze on lifecycle transitions (
allocation_mode,registration_opens_atafter draft; policies freeze on open). - UI: Turbo Frames for inline editing; flash messages for validation errors and freeze violations; feature flag
registration_campaigns_enabled; disabled fields for frozen attributes. - Refs: Campaign lifecycle & freezing, State diagram
- Acceptance: Teachers can create draft campaigns, add policies, open campaigns (with policy validation); campaigns cannot be deleted when open/processing; freezing rules enforced with clear error messages; frozen fields disabled in UI; feature flag gates UI.
- Scope: Manage registerable items within a campaign, including cohorts.
- Controllers:
Registration::ItemsController(nested routes under campaigns). - Freezing: Items cannot be removed when
status != :draft(prevents invalidating existing registrations); adding items always allowed. - UI: Turbo Frames for inline item addition/removal; capacity editing; delete button disabled for items when campaign is open.
- Refs: Item model, Freezing rules
- Acceptance: Teachers can add items anytime; items cannot be removed if campaign is open or has registrations for that item; capacity edits validated.
- Scope: Student registration for single-item campaigns (e.g., one tutorial per lecture).
- Controllers:
Registration::UserRegistrationsController(create/destroy). - Logic: FCFS mode with capacity checks; policy evaluation on create.
- Freezing: Item capacity can increase anytime; can decrease only if
new_capacity >= confirmed_count(prevents revoking confirmed spots). - UI: Registration button; Turbo Stream updates for immediate feedback; capacity editing validates against confirmed count.
- Refs: FCFS mode, Freezing rules
- Acceptance: Students can register for open campaigns; capacity enforced; policy violations shown with error messages; confirmed status set immediately; capacity decrease blocked if it would revoke spots.
- Scope: Extend PR-3.3 for multi-item campaigns (e.g., tutorial selection from multiple options).
- Controllers: Extend
Registration::UserRegistrationsControllerto handle item selection. - UI: Item selection dropdown; Turbo Stream for dynamic item list updates.
- Refs: Multi-item campaigns
- Acceptance: Students can select from available items; capacity per item enforced; switching items updates previous registration.
- Scope: "New Campaign" wizard that forces an explicit choice of Registration Pattern.
- Controllers:
Registration::CampaignsController#newhandles?pattern=param. - Logic:
- Step 1 (Pattern Selection): User chooses between:
- Group Track (Pattern 1): Creates "Group Registration" campaign (Tutorials/Talks) + optional "Special Groups" campaign (Cohorts).
- Enrollment Track (Pattern 2): Creates "Course Enrollment" campaign (Lecture).
- Mixed Track (Pattern 3): Creates both (gated by explicit acknowledgement).
- Step 2 (Configuration):
- For Group Track: Select group source (Tutorials vs Talks), optionally add Cohorts.
- For Enrollment Track: Confirm Lecture target.
- Step 1 (Pattern Selection): User chooses between:
- UI: Modal with 3 distinct choices; "Mixed Track" is visually distinct/gated.
- Acceptance: Wizard guides user through pattern selection; correctly creates 1 or 2 campaigns based on choice; enforces acknowledgement for Mixed Track.
- Scope: UI for students to rank items by preference.
- Controllers: Extend
Registration::UserRegistrationsControllerwithupdate_preferencesaction. - UI: Drag-and-drop ranking interface; persisted as JSONB array in
preferencescolumn. - Refs: Preference mode
- Acceptance: Students can rank items; preferences saved; cannot submit incomplete rankings.
- Scope: Implement minimal roster persistence for
materialize_allocation!. - Models: Add
source_campaign_idtotutorial_membershipsjoin table. - Concerns: Implement
Roster::Rosterablewithallocated_user_ids,materialize_allocation!,roster_entries,mark_campaign_source!. - Implementation in Tutorial: Override
allocated_user_idsto delegate toroster_user_ids; implementmaterialize_allocation!usingreplace_roster!pattern. - Refs: Rosterable concern, Tutorial implementation
- Acceptance: Tutorial implements Rosterable methods;
materialize_allocation!replaces roster entries tagged with campaign;allocated_user_idsreturns current roster user IDs.
- Scope: Wire solver and finalize end-to-end.
- Services:
Registration::Allocation::Solver(delegates to CP-SAT or placeholder greedy),Registration::FinalizationGuard. - Controllers:
Registration::AllocationController(trigger/retry/finalize actions). - Background:
AllocationJobruns solver and updates UserRegistration statuses via Turbo Streams. - Logic: On finalize, call
FinalizationGuard#check!, thenmaterialize_allocation!for each confirmed user. - Freezing: Item capacity freezes once
status == :completed(results published); can be adjusted freely duringdraft,open,closedstates. - Refs: Solver, Finalization, Freezing rules
- Acceptance: Teachers can trigger allocation; results streamed to UI; finalize materializes rosters; capacity changes blocked after completion; unconfirmed users stay in limbo; confirmed users added to rosters via
materialize_allocation!.
- Scope: Post-allocation roster management for Sub-Groups (Tutorial/Cohort).
- Controllers:
Roster::MaintenanceController(move/add/remove actions). - Logic: Support
Tutorial,TalkandCohortas rosterable types. - UI: Roster Overview with detail views for individual groups; candidates panel (unassigned users from campaign); basic capacity enforcement.
- Refs: Roster maintenance
- Acceptance: Teachers can move students between tutorials/talks/cohorts; capacity enforced; candidates panel lists unassigned users; manual add/remove works for groups.
- Scope: Implement Lecture Roster as the superset of all sub-groups.
- Backend:
- Implement
Lecture#ensure_roster_membership!. - Update
Tutorial/Talk/Cohort#materialize_allocation!to propagate users to Lecture. - Update
Roster::MaintenanceServiceto handle Enrollment/Drop logic (cascading delete).
- Implement
- UI: "Enrollment" tab in
RosterOverviewComponentshowing all lecture members; "Unassigned" status calculation. - Refs: Superset Model, Enrollment Track
- Acceptance: Adding user to tutorial adds them to lecture; Removing from lecture removes from tutorial; Enrollment tab lists all students.
- Scope: Add
self_materialization_modeenum toRosterable; implement permission methods; admin UI for configuration. - Backend:
- Migration: Add
self_materialization_modeenum column to tutorials, talks, cohorts, lectures. - Update
Roster::Rosterableconcern withcan_self_add?,can_self_remove?,self_add!,self_remove!. - Validation: Block enabling mode during active campaigns (non-planning, non-completed).
- Migration: Add
- Admin UI:
- Add mode selector dropdown to roster management UI (per-group basis).
- Four options: disabled/add_only/remove_only/add_and_remove.
- Turbo Frame update on change; validation errors shown inline.
- Feature flag:
self_materialization_enabled.
- Refs: Self-materialization, Rosterable concern
- Acceptance: Backend methods work; validation enforced; admin can configure mode per tutorial/cohort/talk; changes blocked with clear error if campaign active; feature flag gates both backend and UI.
- Scope: Student-facing join/leave buttons.
- Controllers:
Roster::SelfMaterializationController(join/leave actions). - UI:
- Join/Leave buttons on Tutorial/Cohort/Talk show pages.
- Turbo Stream updates for roster list.
- Buttons hidden when mode is
disabled. - Capacity/duplicate guards with error messages.
- Refs: Self-materialization
- Acceptance: Students can join/leave when enabled; buttons respect mode settings; capacity enforced; clear error messages for violations; feature flag gates UI.
- Scope: Tutors can view rosters for their assigned groups.
- Abilities: Update CanCanCan to allow read-only roster access for tutors.
- UI: Tutors see Detail view without edit actions.
- Refs: Abilities
- Acceptance: Tutors can view rosters for their tutorials; cannot edit; exams do not show candidates panel.
- Scope: Add students to rosters manually.
- Controllers: Extend
Roster::MaintenanceControllerwithadd_studentaction. - UI: "Add student" button on Overview; search input for arbitrary student addition.
- Refs: Manual operations
- Acceptance: Teachers can add students from candidates or search; capacity enforced; duplicate prevention.
- Scope: Email notifications for roster changes affecting students.
- Mailer:
Roster::ChangeMailerwith methods:added_to_group,removed_from_group,moved_between_groups. - Triggers: Called from
Roster::MaintenanceService,Registration::Campaign#finalize!, andRoster::SelfMaterializationController. - Content: Email includes group name, lecture context, action taken, timestamp; for moves, includes both source and target groups.
- Configuration: Feature flag
roster_notifications_enabled; teacher toggle per lecture for notification verbosity (all changes vs finalization-only). - Background: Deliver via
ActionMailer::DeliveryJob(async). - Refs: Roster maintenance
- Acceptance: Students receive emails on add/remove/move; emails queued asynchronously; feature flag gates delivery; teachers can configure notification timing per lecture.
- Scope: Background job to verify roster consistency.
- Job:
AllocatedAssignedMatchJobcomparesItem#assigned_userswithRegisterable#allocated_user_ids. - Monitoring: Logs mismatches for admin review.
- Refs: Integrity invariants
- Acceptance: Job runs nightly; reports mismatches; no auto-fix (manual review required).
- Scope: Extract stream logic from controllers into
Turbo::LectureStreamService. - Pattern: Controllers declare "what happened" (e.g.,
roster_changed); service returns appropriate streams. - Refactor:
TutorialsController,CohortsController,Registration::CampaignsController,Roster::MaintenanceController. - Implementation:
Turbo::LectureStreamServicewith methods:roster_changed,campaign_changed,enrollment_changed.- Each method returns array of turbo streams for all dependent UI components.
- Controllers call service instead of building streams inline.
- Refs: Turbo Streams
- Acceptance: Controllers contain no DOM IDs or partial paths; all cross-tab updates centralized in service; adding new dependent views requires editing only the service.
- Scope: Create
assessment_assessments,assessment_tasks,assessment_participations,assessment_task_points. - Migrations:
20251105000000_create_assessment_assessments.rb20251105000001_create_assessment_tasks.rb20251105000002_create_assessment_participations.rb20251105000003_create_assessment_task_points.rb
- Refs: Assessment models
- Acceptance: Migrations run; models have correct associations; no existing tables altered.
- Scope: Core assessment concerns plus Assignment/Talk integration.
- Backend:
- Create
Assessment::Assessableconcern (interface for all assessable types) - Create
Assessment::Pointableconcern (for task-based grading: assignments, exams) - Create
Assessment::Gradableconcern (for final grade: assignments, exams, talks) - Include
Assessable + Pointable + Gradablein Assignment model (behind feature flag) - Include
Assessable + Gradablein Talk model (behind feature flag) - Add
after_create :setup_assessmenthooks - Implement
seed_participations_from_roster!for Assignment and Talk
- Create
- Feature Flag:
assessment_grading_enabled(per-lecture) - Behavior: When enabled, new assignments/talks automatically create Assessment record + Participations
- Refs: Assessment::Assessable, Pointable, Gradable
- Acceptance: All three concerns exist; Assignment has all three; Talk has Assessable+Gradable; participations seeded on creation; feature flag gates behavior; old assignments unaffected.
- Scope: Complete assessment management UI without grading interface.
- Controllers:
Assessment::AssessmentsController(full CRUD),Assessment::TasksController(nested CRUD),Assessment::ParticipationsController(index only) - UI:
- "New Assessment" form (type/mode selection, schedule)
- Index page (list, filter, status badges)
- Show page with tabs (Overview, Settings, Tasks, Participants)
- Task management (add/edit/reorder problems)
- Participation list (auto-seeded from PR-7.1)
- Limitations: No point entry, no grade calculation, no result publication (deferred to PR-8.x)
- Feature Flag: Same
assessment_grading_enabledflag gates entire UI - Refs: Assessment controllers, Views
- Acceptance: Teachers can create assessments via UI; participations visible; tasks configurable; no grading actions available; feature flag gates access.
- Scope: Add
assessment_idto submissions table; maintain backward compatibility withassignment_id. - Migration:
- Add
assessment_idcolumn (UUID, nullable) tosubmissionstable - Add foreign key constraint on
assessment_id - Keep existing
assignment_idcolumn and constraint (legacy support)
- Add
- Model Changes:
belongs_to :assignment, optional: true(legacy)belongs_to :assessment, optional: true(new)- Validation: require one or the other (XOR pattern)
- Helper method:
grading_contextreturns assessment or assignment
- Controller Changes:
- For new submissions (when feature flag enabled): set
assessment_id - For old submissions: continue using
assignment_id - Views use
submission.grading_contextfor navigation
- For new submissions (when feature flag enabled): set
- Strategy: New submissions under feature flag use assessment_id; old submissions remain unchanged; both work simultaneously
- Refs: Submission dual-column pattern
- Acceptance: Old submissions still work; new submissions link to assessments; no data loss; both columns coexist; feature flag determines which column is populated for new records.
- Scope: Introduce Exam model with basic functionality (no grade schemes yet).
- Migrations:
20251110000000_create_exams.rb
- Backend:
- Create
Exammodel with concerns:Registration::Registerable,Roster::Rosterable,Assessment::Assessable,Assessment::Pointable,Assessment::Gradable - Implement
materialize_allocation!(delegates toreplace_roster!) - Implement
allocated_user_ids(returns roster user IDs) - Implement
seed_participations_from_roster!
- Create
- Controllers:
ExamsController(CRUD, scheduling) - Registration: Extend
Registration::CampaignsControllerandRegistration::UserRegistrationsControllerto support lecture-hosted exam campaigns - UI: Basic exam creation/editing form; exam registration flows (reuses campaign views); FCFS and preference-based modes
- Limitations: No grading UI, no grade schemes, no point entry (deferred to PR-8.3 and Step 9)
- Rationale: Having exams around enables unified point entry UI design in PR-8.3
- Feature Flag: Same
assessment_grading_enabledflag gates exam creation - Refs: Exam model, Exam registration flow
- Acceptance: Exam model exists with all concerns; teachers can create exams and exam campaigns; students can register for exams; no grading functionality yet; feature flag gates UI.
- Scope:
Assessment::GradingServicefor saving points. - Implementation: Fanout pattern creates Participation and TaskPoints per student (or team); supports ANY Pointable (assignments and exams)
- Refs: GradingService
- Acceptance: Service creates participations and task points; handles team grading; validates point ranges; works for Assignment and Exam assessables.
- Scope: Point entry interface that works for both assignments and exams.
- Controllers:
Assessment::GradingController(new/create/update) - UI:
- Grid view with students × tasks
- Inline editing with Turbo Frames
- Design works for Assignment (homework) AND Exam (written test)
- No assignment-specific assumptions (reusable for exams)
- Rationale: Design with both Assignment and Exam in mind from day 1
- Testing: Verify UI works for both assessable types
- Refs: Grading UI mockup
- Acceptance: Teachers can enter points for assignments AND exams; service called on save; results preview shown; UI agnostic to assessable type; feature flag gates UI.
- Scope: Toggle result visibility for students.
- Controllers: Extend
Assessment::AssessmentsControllerwithpublish_resultsandunpublish_resultsactions - UI: Toggle button on assessment show page; works for both assignments and exams
- Refs: Publication workflow
- Acceptance: Teachers can publish/unpublish results; students see results only when published; toggle works via Turbo Frame; works for any assessable type; feature flag gates UI.
- Scope: Update student submission workflow to interact with Assessment::Participation (when feature flag enabled).
- Controllers: Update
SubmissionsControllerto conditionally set participation status - Logic (when
assessment_grading_enabled?):- On submission upload: Find or create
Assessment::Participationfor student(s) - Set
participation.status = :submitted,submitted_at = Time.current - Set
submission.assessment_id(new column from PR-7.3) - For team submissions: Update all team members' participations
- On submission upload: Find or create
- Logic (when flag disabled):
- Use existing submission flow with
assignment_id - No participation records created
- Use existing submission flow with
- Refs: Submission workflow
- Acceptance: Feature flag controls behavior; new submissions link to assessments and update participations; old submissions continue working unchanged; no breaking changes to existing functionality.
- Scope: Student-facing views for published assessment results.
- Controllers:
Assessment::ParticipationsController(index, show for students) - UI:
- Results Overview: Progress dashboard (points earned, graded count, certification status), assignment list with filters (All/Graded/Pending), collapsible older assignments section
- Results Detail: Per-assignment view with task breakdown table, submitted files, tutor feedback, team info, overall progress sidebar
- Published results only (students cannot see unpublished grades)
- Works for both assignments and exams (no exam-specific adjustments needed yet)
- Authorization: Students see only their own participations; results hidden when
assessment.results_published == false - Navigation: Links from assignment pages to results; download feedback PDFs
- Refs: Student results views
- Acceptance: Students can view published results; task points visible; feedback displayed; unpublished assessments hidden; certification status shown; works for any assessable type; feature flag gates access.
- Scope: Finalize exam registration workflows (no functional changes, just documentation).
- Note: Exam registration was introduced in PR-8.1; this PR serves as checkpoint to verify all flows work correctly before adding grade schemes
- Testing: Comprehensive end-to-end tests for exam registration and allocation
- Refs: Exam registration flow
- Acceptance: All exam registration flows tested; campaign creation, student registration, allocation, and finalization work correctly.
- Scope: Create
grade_schemesandgrade_scheme_thresholds. - Migrations:
20251110000001_create_grade_schemes.rb20251110000002_create_grade_scheme_thresholds.rb
- Refs: GradeScheme models
- Acceptance: Migrations run; models have correct validations; percentage-based thresholds supported.
- Scope:
GradeScheme::Applierfor converting exam points to grades. - Implementation: Supports absolute points and percentage-based bands; idempotent application via version_hash; respects manual overrides
- Refs: GradeScheme applier
- Acceptance: Service computes grades from points; handles both absolute and percentage schemes; version_hash prevents duplicate applications.
- Scope: UI for grade scheme configuration and application (layers on top of PR-8.3 point entry).
- Controllers:
GradeScheme::SchemesController(configuration, preview, apply) - UI:
- Distribution analysis (histogram, statistics) based on entered points
- Scheme configuration (two-point auto-generation + manual adjustment)
- Grade preview showing how scheme maps to students
- Apply action (runs GradeScheme::Applier)
- Integration: Uses existing point entry UI from PR-8.3; adds grade scheme layer
- Refs: Exam grading workflow
- Acceptance: Teachers can create and apply grade schemes; preview grade distribution; apply action creates final grades; publication uses existing PR-8.4 toggle; feature flag gates UI.
- Scope: Direct grade entry for talks (no task points, just final grade).
- Controllers:
Assessment::TalkGradingController(new controller for Gradable-only workflow) - UI:
- Student list with grade dropdown (direct grade entry, no tasks)
- Reuses publication toggle from PR-8.4
- Demonstrates Gradable concern without Pointable (talks don't have task breakdowns)
- Rationale: Talks have Assessable+Gradable but NOT Pointable (no task decomposition)
- Refs: Talk grading workflow
- Acceptance: Teachers can assign final grades to talks; no task points involved; publication works; feature flag gates UI.
- Scope: Create Achievement as assessable for non-graded participation.
- Model:
Achievementwithvalue_type(boolean/numeric/percentage) - Concerns:
Assessment::Assessable(but NOT Pointable or Gradable) - Controllers:
AchievementsController(CRUD), extendAssessment::ParticipationsControllerwith achievement marking actions - UI: Checkbox/numeric input for marking; student list view
- Rationale: Achievements track participation but don't contribute to grades
- Refs: Achievement model, Participation tracking
- Acceptance: Achievement model exists; can be linked to assessments; teachers can mark achievements; students see progress; value_type validated; feature flag gates UI.
Status of Steps 11-15: Steps 11 through 15 (Dashboards, Student Performance, Exam Eligibility, and Quality/Hardening) remain at high-level outline stage. These steps build upon the assessment/grading foundation established in Steps 7-10. Detailed PR breakdowns for Steps 11-15 will be added during implementation planning for those phases.
- Scope: Student dashboard with widgets for registrations, grades, exams, deadlines.
- Controllers:
Dashboards::StudentControllerwith widget partials. - Widgets: "My Registrations", "Recent Grades", "Upcoming Exams", "Deadlines".
- Refs: Student dashboard mockup
- Acceptance: Students see dashboard; widgets show data from new tables including exam registrations; exam eligibility widget hidden (added in Step 14).
- Scope: Teacher dashboard with widgets for campaigns, rosters, grading, exams.
- Controllers:
Dashboards::TeacherControllerwith widget partials. - Widgets: "Open Campaigns", "Roster Management", "Grading Queue", "Exam Management".
- Refs: Teacher dashboard mockup
- Acceptance: Teachers see dashboard; widgets show actionable items including exam grading; certification widget hidden (added in Step 14).
- Scope: Create
student_performance_records,student_performance_rules,student_performance_achievements,student_performance_certifications. - Migrations:
20251120000000_create_student_performance_records.rb20251120000001_create_student_performance_rules.rb20251120000002_create_student_performance_achievements.rb20251120000003_create_student_performance_certifications.rb
- Refs: Student Performance models
- Acceptance: Migrations run; models have correct associations; unique constraints on certifications.
- Scope:
StudentPerformance::ComputationServiceto aggregate performance data. - Implementation: Reads from
assessment_participationsandassessment_task_points; writes tostudent_performance_records. - Refs: ComputationService
- Acceptance: Service computes points and achievements; upserts Records; handles missing data gracefully.
- Scope:
StudentPerformance::Evaluatorto generate certification proposals. - Implementation: Reads Records and Rules; returns proposed status (passed/failed) per student.
- Refs: Evaluator
- Acceptance: Evaluator generates proposals; does NOT create Certifications; used for bulk UI only.
- Scope:
StudentPerformance::RecordsControllerfor viewing performance data. - Controllers: Index/show actions for Records.
- UI: Table view with points, achievements, computed_at timestamp.
- Refs: RecordsController
- Acceptance: Teachers can view Records; no decision-making UI; feature flag gates access.
- Scope:
StudentPerformance::CertificationsControllerfor teacher certification. - Controllers: Index (dashboard), create (bulk), update (override), bulk_accept.
- UI: Certification dashboard with proposals; bulk accept/reject; manual override with notes.
- Refs: CertificationsController
- Acceptance: Teachers can review proposals; bulk accept; override with manual status; remediation workflow for stale certifications.
- Scope:
StudentPerformance::EvaluatorControllerfor proposal generation. - Controllers:
bulk_proposals,preview_rule_change,single_proposal. - UI: Modal for rule change preview showing diff of affected students.
- Refs: EvaluatorController
- Acceptance: Teachers can generate proposals; preview rule changes; does NOT create Certifications automatically.
- Scope: Add
student_performancepolicy kind toRegistration::PolicyEngine. - Implementation:
Registration::Policy#eval_student_performancechecksStudentPerformance::Certification.find_by(...).status. - Phase awareness: Returns different errors for registration (missing/pending) vs finalization (failed).
- Refs: Policy evaluation
- Acceptance: Policy checks Certification table; phase-aware logic; tests use Certification doubles.
- Scope: Add certification completeness checks to campaign lifecycle.
- Controllers: Update
Registration::CampaignsController#opento check for missing/pending certifications; block if incomplete. - Update
Registration::AllocationController#finalizeto check for missing/pending; auto-reject failed certifications. - Refs: Pre-flight validation
- Acceptance: Campaigns cannot open without complete certifications; finalization blocked if pending; failed certifications auto-rejected.
- Scope: Wire eligibility checks into exam registration UI.
- Controllers: Extend existing exam registration controllers to handle student_performance policy errors.
- UI: Eligibility status displays; blocked registration with clear messaging; links to performance overview.
- Refs: Exam registration flow
- Acceptance: Students see eligibility status; policy blocks ineligible users; clear error messages; links to certification details; feature flag gates UI.
- Scope: UI for teachers to resolve pending certifications during finalization.
- Controllers: Add remediation actions to
StudentPerformance::CertificationsController. - UI: Remediation modal during finalization showing pending students; quick-resolve actions; bulk accept/reject.
- Refs: Remediation workflow
- Acceptance: Teachers can resolve pending certifications inline during finalization; finalization retries after resolution; auto-rejection of failed students.
- Scope: Add student performance and exam registration widgets.
- Widgets: "Exam Eligibility Status", "Performance Overview".
- Refs: Student dashboard complete
- Acceptance: Students see eligibility status; performance summary; links to certification details.
- Scope: Add certification and exam management widgets.
- Widgets: "Certification Pending List", "Eligibility Summary".
- Refs: Teacher dashboard complete
- Acceptance: Teachers see pending certifications; summary of eligible students; links to remediation UI.
- Scope: Create integrity jobs for student performance.
- Jobs:
PerformanceRecordUpdateJob(recompute Records after grading),CertificationStaleCheckJob(flag stale certifications),AllocatedAssignedMatchJob(verify roster consistency). - Refs: Background jobs
- Acceptance: Jobs run on schedule; log issues; no auto-fix for critical data.
- Scope: Admin UI for monitoring data integrity.
- Controllers:
Admin::IntegrityControllerwith dashboard views. - Widgets: Pending certifications, stale certifications, roster mismatches.
- Refs: Monitoring
- Acceptance: Admins see integrity metrics; drill-down to affected records; export reports.