Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Integrity & Invariants

This chapter documents the key invariants and integrity constraints that ensure system correctness throughout the semester lifecycle.

Enforcement Strategy

When feasible, enforce constraints at the database level. For complex business rules, use application-level validations and background reconciliation jobs.


1. Registration & Allocation

Database Constraints

# One confirmed submission per user per campaign
add_index :registration_submissions,
          [:registration_campaign_id, :user_id],
          unique: true,
          where: "status = 'confirmed'",
          name: "idx_unique_confirmed_submission"

# Unique preference ranks per user per campaign
add_index :registration_submissions,
          [:user_id, :registration_campaign_id, :preference_rank],
          unique: true,
          where: "preference_rank IS NOT NULL",
          name: "idx_unique_preference_rank"

Application-Level Invariants

InvariantEnforcement
Registration::UserRegistration.status ∈ {pending, confirmed, rejected}Enum validation
At most one confirmed submission per (user, campaign)Unique index
Preference-based campaigns: each pending submission has unique rankUnique index + validation
Capacity never exceeded at allocationAllocation algorithm respects registerable.capacity
Campaign finalized exactly oncefinalize! idempotent with status check
assigned_count matches confirmed submissionsBackground reconciliation job
Assigned users = confirmed UserRegistrations (registration data)Count from Registration::UserRegistration.where(status: :confirmed)
Allocated users = materialized roster (domain data)Count from rosterable.allocated_user_ids
After finalization: assigned users = allocated usersMaterialization ensures consistency

2. Rosters & Materialization

Core Invariants

InvariantDetails
Initial roster snapshotmaterialize_allocation! sets roster to match confirmed submissions
Historical integrityPost-allocation roster changes don't mutate Registration::UserRegistration records
Atomic operationsRoster::MaintenanceService uses transactions
Capacity enforcementEnforced unless explicit override by staff
Audit trailAll roster changes logged with actor, reason, timestamp

Reconciliation

Background job periodically checks:

  • Roster user count vs. capacity limit
  • Orphan roster entries (user deleted but still in roster)

3. Assessments & Grading

Database Constraints

# One participation per (assessment, user)
add_index :assessment_participations,
          [:assessment_id, :user_id],
          unique: true,
          name: "idx_unique_participation"

# One task point per (participation, task)
add_index :assessment_task_points,
          [:participation_id, :task_id],
          unique: true,
          name: "idx_unique_task_point"

# Foreign key integrity
add_foreign_key :assessment_tasks, :assessments
add_foreign_key :assessment_task_points, :assessment_tasks, column: :task_id
add_foreign_key :assessment_task_points, :assessment_participations, column: :participation_id

Application-Level Invariants

InvariantEnforcement
Participation.total_points = sum(task_points.points)Automatic recomputation on TaskPoint save
TaskPoint.points ≤ Task.max_pointsValidation on save
Task records exist only if Assessment has tasksValidation
Results visible only when Assessment.results_published = trueController authorization
Participation.submitted_at persists across status changesNever overwritten after initial set

Multiple Choice Extension

For MC exam-specific constraints, see the Multiple Choice Exams chapter.


4. Student Performance & Certification

Database Constraints

# One performance record per (lecture, user)
add_index :student_performance_records,
          [:lecture_id, :user_id],
          unique: true,
          name: "idx_unique_performance_record"

# One certification per (lecture, user)
add_index :student_performance_certifications,
          [:lecture_id, :user_id],
          unique: true,
          name: "idx_unique_certification"

# Foreign key integrity
add_foreign_key :student_performance_certifications,
                :student_performance_records,
                column: :record_id,
                optional: true
add_foreign_key :student_performance_certifications,
                :student_performance_rules,
                column: :rule_id,
                optional: true

Application-Level Invariants

InvariantEnforcement
One Record per (lecture, user)Unique index
One Certification per (lecture, user)Unique index
Records store only factual data (points, achievements)No eligibility interpretation in Record model
Certifications store teacher decisions (passed/failed/pending)Status enum validation
Certification.status ∈ {passed, failed, pending}Enum validation
Campaigns cannot open with pending certificationsPre-flight validation in Campaign model
Campaigns cannot finalize with stale certificationsPre-finalization validation
Manual certification requires note fieldValidation when certified_by present
Record recomputation preserves existing CertificationsCertification stability—only flagged for review, not auto-updated
Certification certified_at timestamp immutableSet once, never changed (new certification created for updates)

Certification Lifecycle Invariants

PhaseInvariantDetails
Before RegistrationAll students have CertificationsPre-flight check blocks campaign opening
During RegistrationNo pending certifications existAll must be passed or failed
Runtime Policy CheckPolicy looks up Certification.statusNo runtime recomputation
Grade ChangeRecord recomputed, Certification flagged staleTeacher must review before next campaign
Rule ChangeAll Certifications flagged for reviewTeacher sees diff and must re-certify

5. Grade Schemes

Invariants

InvariantDetails
At most one active scheme per assessmentAssessment belongs_to :grade_scheme
Identical version_hash = no-opApplier checks hash before reapplication
Manual overrides preservedOverridden participations skipped during reapplication
Bands cover full rangeValidation ensures 0.0 to 1.0 coverage

6. Allocation Algorithm

Preference-Based (Flow Network)

InvariantDetails
Each user assigned to ≤ 1 itemFlow solver ensures exclusivity
Total assigned to item ≤ capacityCapacity constraint in network
Unassigned users get dummy edgeIf allow_unassigned = true
No partial writes on failureTransaction rollback on solver error

First-Come-First-Served

InvariantDetails
Submissions processed in timestamp orderOrdered query by created_at
Capacity checked atomicallyDatabase-level row locking
Concurrent submissions handled safelyPessimistic locking or retry logic

7. Policy Engine

Invariants

InvariantDetails
Policies evaluated in ascending position orderStable sort ensures deterministic evaluation
First failure short-circuitsRemaining policies not evaluated
No side effects on policy failureRead-only policy checks
Policy trace retained per requestFor debugging and audit purposes

8. Data Consistency Reconciliation

JobPurposeFrequency
RecountAssignedJobRecompute assigned_count from confirmed submissionsHourly
ParticipationTotalsJobVerify total_points matches sum of task pointsDaily
PerformanceRecordUpdateJobRecompute Records after grade changesAfter grade changes
CertificationStaleCheckJobFlag certifications for review when Records changeAfter record updates
OrphanTaskPointsJobDetect task points with missing participation/taskWeekly
RosterIntegrityJobCheck roster user counts vs. capacitiesDaily
AllocatedAssignedMatchJobVerify allocated_user_ids matches assigned users post-finalizationWeekly

9. Idempotency Patterns

OperationIdempotency Strategy
Campaign.finalize!Check status != :finalized before proceeding
materialize_allocation!Replace entire roster (not additive)
GradeScheme::Applier.apply!Compare version_hash; skip if unchanged
StudentPerformance::ComputationService.compute!Upsert pattern preserves overrides
Roster::MaintenanceService operationsEach operation atomic with validation

10. Security & Authorization

Access Control

These rules must be enforced via authorization layer (e.g., Pundit policies):

ResourcePermissionEnforcement
CampaignsCreate/modifyStaff only
PoliciesCreate/modifyStaff only
SubmissionsCreateUser for self, open campaign
RostersModifyStaff only via MaintenanceService
GradesEnter/modifyStaff/tutors only
Eligibility overridesSetStaff only with audit trail

11. Monitoring & Alerts

Key Metrics

MetricThresholdActionExplanation
Orphan submissions= 0Alert immediatelySubmissions without a valid registration_item_id indicate broken foreign keys or data corruption
Allocation failures (last 24h)> 0Alert staffFailed registration assignments need manual review; may indicate capacity or constraint issues
Count drift per item> 5Trigger recount jobDifference between assigned_count cache and actual roster count suggests cache staleness
Pending certifications during active campaigns> 0Alert staffCampaigns should not have pending certifications; blocks campaign operations
Stale certifications> 10% of totalAlert staffHigh staleness rate suggests Records are being recomputed but Certifications not reviewed
Performance record age during grading period> 48hTrigger recomputationStale Records mean certifications are based on outdated data

Count Drift Metric

The "count drift" metric compares the cached assigned_count field on registration items against the actual number of confirmed roster entries. A drift > 5 suggests the cache is out of sync with reality, which can happen after manual roster modifications or failed callbacks. The recount job refreshes these cached values.

Extra Points Allowed

Points exceeding task maximum are intentionally permitted to support extra credit scenarios and bonus points. This is not considered an error condition.


12. Audit Checklist

Use this checklist for manual verification:

  • Random sample: confirmed submission IDs match roster user IDs (for recently finalized campaigns)
  • Random sample: total_points matches sum(task_points.points) for assessments
  • All certifications with manual overrides have non-null note field
  • No pending certifications exist during active registration campaigns
  • All lecture students have Certifications before exam campaign opens
  • Registration policy position values are continuous (no gaps) per campaign
  • Roster changes have audit trail entries
  • No orphan task points (all reference valid participation + task)
  • Assigned users (registration data) match allocated users (roster data) after finalization
  • Certifications are not auto-updated when Records change (stability check)