# Strong Enrollment, Registration, Promotion, and Section Placement Plan

## 1. Purpose

This plan defines a stronger, safer lifecycle for student registration, enrollment, promotion, and next-class section placement.

The goal is to separate concepts that are currently too easy to mix together:

- Student identity
- New-student registration
- Returning-student enrollment
- Academic pass/fail decision
- Promotion eligibility
- Class/section placement
- Historical display and auditability

A strong system must not treat all of these as one status field wearing too many hats. That is how school software becomes a haunted spreadsheet.

---

## 2. Core Principle

The system must distinguish between:

| Concept | Meaning | Source of Truth |
|---|---|---|
| Student identity | The person/student record | `students` |
| New-student registration | A new family applies for admission | registration/application tables |
| New-student approval | Admin approves the new student | admin registration/enrollment flow |
| Returning-student enrollment | Parent confirms child is continuing next year | returning enrollment record |
| Academic decision | Student passed, failed, retained, or needs review | `student_decisions` |
| Promotion eligibility | Student may move to next class | derived from `student_decisions` |
| Section placement | Student is assigned to a class section | section placement batch |
| Historical display | Old records remain visible as originally stored | legacy records/tables |

Promotion must not directly mean enrollment. Enrollment must not directly mean class placement. Class placement must not decide pass/fail. Each process gets its own job, because apparently databases behave better when we stop asking one column to run a school.

---

## 3. Backward Compatibility Requirement

The new system must be additive, not destructive.

Existing registration, enrollment, assignment, and promotion data must remain displayable. Old rows must not be silently reinterpreted or recalculated under the new policy.

### Required rules

```text
Old records remain visible.
Old student/class assignments remain readable.
Historical enrollment state is not silently changed.
Historical promotion results are not silently recalculated.
New lifecycle rules apply only to future/new workflow records or explicitly migrated records.
```

### Recommended legacy metadata

Where needed, add metadata such as:

```text
lifecycle_mode = legacy | strong
lifecycle_policy_version = legacy_v1 | strong_v1
```

Existing rows should default to:

```text
lifecycle_mode = legacy
lifecycle_policy_version = legacy_v1
```

This allows the UI to show old data clearly without pretending old rows followed the new workflow.

---

## 4. Student Lifecycle Types

The system must support two main student flows.

## 4.1 New Student Flow

A new student is not already part of the school’s prior-year enrollment.

New-student flow:

```text
Parent submits new-student registration
→ Admin reviews application
→ Admin approves or rejects
→ Approved student enters placement pool
→ Student is assigned to class section
→ Enrollment is completed
```

Admin approval is required for new students.

### New-student states

```text
application_draft
application_submitted
admin_review_pending
approved
rejected
waitlisted
placement_pending
placed_in_class
enrollment_completed
```

---

## 4.2 Returning Student Flow

A returning student already exists and was enrolled in the previous school year/class.

Returning-student flow:

```text
Old class/year finalized
→ `student_decisions` records pass/fail/retain/review decision
→ If passed/promoted, student becomes eligible to continue
→ Parent completes returning-student enrollment
→ System validates eligibility
→ Student enters placement pool
→ Student is assigned to class section
→ Enrollment is completed
```

Admin approval is not required for normal returning students.

Admin review is required only for exceptions.

### Returning-student states

```text
academic_not_reviewed
eligible_to_continue
retained
parent_enrollment_pending
parent_enrollment_completed
placement_pending
placed_in_class
enrollment_completed
exception_review_required
```

---

## 5. Pass/Fail Source of Truth

Promotion eligibility must come from `student_decisions`.

The placement system must not recalculate pass/fail. Its job is placement, not academic judgment. Otherwise it becomes judge, registrar, scheduler, and disaster generator.

### Required rule

```text
Promotion placement must read academic eligibility from `student_decisions`.
A student is eligible for next-class placement only when `student_decisions` marks the student as passed/promoted for the completed school year/class.
Failed, retained, pending, or missing-decision students must not enter the automatic next-class placement pool.
```

### Eligibility from `student_decisions`

| Decision | Placement behavior |
|---|---|
| passed/promoted | Eligible for next-class placement after parent enrollment |
| failed/retained | Excluded from automatic next-class placement |
| pending | Exception report |
| missing | Exception report |
| manual review | Exception review |
| withdrawn/inactive | Excluded unless admin override |

### Required exception buckets

The system should report:

```text
Passed but parent enrollment missing
Passed and parent enrollment completed
Failed/retained
Missing student decision
Pending student decision
Decision conflicts with enrollment request
Withdrawn/inactive student
Duplicate target-year enrollment
```

---

## 6. Parent Enrollment Requirement for Returning Students

A returning student must not be placed into the next year/class until the parent completes returning-student enrollment.

### Required rule

```text
For returning students, promotion eligibility plus parent enrollment completion is sufficient to continue enrollment into the next school year, subject to system validation and class placement.
Admin approval is only required for exceptions.
```

### Returning enrollment should capture

```text
student_id
parent_id
target_school_year
requested_grade_level_id
returning_student = true
required forms completed
emergency contact confirmation
medical/contact update confirmation
agreement/consent confirmations
submission timestamp
status
```

### Returning enrollment statuses

```text
not_started
in_progress
submitted
completed
expired
exception_review_required
```

Normal returning-student continuation should use:

```text
parent_enrollment_completed
```

not:

```text
admin_approved
```

Admin approval is for new students and exceptions, not normal returning students.

---

## 7. When Admin Review Is Required for Returning Students

Returning students should flow automatically only when clean.

Admin review is required when:

```text
student failed but parent tries to enroll in next grade
student is retained and needs repeat placement
student has no linked parent/guardian
required forms are incomplete after deadline
requested class/grade conflicts with eligibility
capacity override is needed
manual promotion override is requested
duplicate enrollment exists
student was withdrawn or inactive
student_decisions is missing or pending
```

This keeps normal cases fast and sends only abnormal cases to humans, who are expensive, inconsistent, and sometimes looking for the wrong spreadsheet.

---

## 8. Placement Pool Construction

The final placement pool is built from two sources:

```text
eligible returning students
+ approved new students
```

### 8.1 Returning student inclusion rule

A returning student enters the placement pool only if:

```text
`student_decisions` marks student as passed/promoted
parent completed returning-student enrollment
student is not already placed in the target year
target grade/class matches promotion path
student is active/not withdrawn
```

### 8.2 New student inclusion rule

A new student enters the placement pool only if:

```text
new-student registration/enrollment is admin-approved
student is not already placed in the target year
target grade/class is valid
```

---

## 9. Configurable Section Capacity

The section capacity must not be hardcoded.

The maximum number of students per generated section must be read from the `configuration` table as a key/value setting.

### Required configuration key

Recommended key:

```text
promotion.section_capacity = 20
```

### Configuration table example

| key | value | type | description |
|---|---:|---|---|
| `promotion.section_capacity` | `20` | integer | Maximum number of students per generated promoted section |

### Validation

```text
promotion.section_capacity must be an integer
promotion.section_capacity must be greater than 0
recommended minimum: 5
recommended maximum: 40 unless admin override
```

### Fallback rule

If the key is missing:

```text
section_capacity = 20
```

The fallback protects the system, but missing config should still be logged as a warning. Silent defaults are convenient right up until nobody knows why sections were created that way.

### Snapshot rule

The system must store the capacity used at generation time:

```text
section_capacity_used
```

Optional but recommended:

```text
configuration_snapshot_json
```

Example:

```json
{
  "promotion.section_capacity": 20,
  "placement.algorithm": "score_band_balanced_v1"
}
```

This prevents old placement batches from changing meaning if the capacity is later changed to 22, 25, or whatever fresh number policy invents.

---

## 10. Section Count Rule

Let:

```text
section_capacity = configuration['promotion.section_capacity'] ?? 20
total_students = count(placement_pool)
```

Then:

```text
If total_students <= section_capacity:
    create one section

If total_students > section_capacity:
    section_count = ceil(total_students / section_capacity)
```

Examples when `promotion.section_capacity = 20`:

| Total students | Section count |
|---:|---:|
| 1-20 | 1 |
| 21-40 | 2 |
| 41-60 | 3 |
| 61-80 | 4 |

---

## 11. Score Bands for Placement

Students must be grouped into score bands for balanced section placement.

### Bands

| Band | Score range |
|---|---:|
| A | 90-100 |
| B | 80-89 |
| C | 70-79 |
| D | 60-69 |

Students below 60 are not eligible for automatic next-class promotion unless an authorized override exists.

### Score source by student type

| Student type | Eligibility source | Band source |
|---|---|---|
| Returning student | `student_decisions` pass/promote | final promotion score |
| Failed/retained student | `student_decisions` fail/retain | excluded from next-class automatic placement |
| New student with placement assessment | approved new-student enrollment | assessment score band |
| New student without placement assessment | approved new-student enrollment | D band by default |

---

## 12. New Student Default Band Rule

New students do not always have prior school scores. If no approved placement/assessment score exists, they must be assigned to the D band for balancing purposes.

### Required rule

```text
New students approved for enrollment are included in the target class placement pool.
If a new student does not have an approved placement or assessment score, the system assigns the student to the D score band, 60-69, for section-balancing purposes only.
This default band assignment must not be stored as an academic grade.
It must be stored as a placement-band classification with the reason `new_student_default_band`.
```

Do not invent a fake numeric score like 65. That would contaminate reports later, because somebody will export it and believe it is real. Humans keep doing that. We design around it.

### Recommended fields

```text
placement_band = D
placement_score = null
placement_band_reason = new_student_default_band
```

If a new student has an approved placement score:

```text
placement_band = derived from placement score
placement_score = approved assessment score
placement_band_reason = new_student_assessment
```

---

## 13. Balanced Section Placement Rule

When there are multiple sections, the system must balance:

1. Total number of students per section.
2. Score-band mix across sections.

The system should not simply divide each score band and accidentally create sections with different total sizes.

### Required rule

```text
When distributing score bands across multiple sections, the system must prioritize equal total section size while keeping each score band as evenly distributed as possible.
Remainders from score bands should be assigned using a balancing algorithm that gives the next student to the section with the lowest current total, not simply to the next section in fixed order.
```

### Target section sizes

Given:

```text
total_students
section_count
```

Calculate:

```text
base_size = floor(total_students / section_count)
remainder = total_students % section_count
```

Then:

```text
first `remainder` sections may have base_size + 1 students
remaining sections have base_size students
```

Example:

```text
32 students
2 sections
target size = 16 and 16
```

Example:

```text
43 students
3 sections
target size = 15, 14, 14
```

---

## 14. Balanced Placement Algorithm

Use a deterministic algorithm.

### Algorithm: score_band_balanced_v1

```text
1. Load section_capacity from configuration.
2. Validate section_capacity.
3. Build placement pool:
   - returning students passed/promoted in `student_decisions`
   - returning parent enrollment completed
   - approved new students
4. Exclude:
   - failed/retained students
   - missing/pending decision students
   - parent enrollment missing for returning students
   - duplicate target-year placements
5. Count placement pool.
6. If total_students <= section_capacity:
      create one section and assign all students.
7. If total_students > section_capacity:
      section_count = ceil(total_students / section_capacity).
8. Calculate target section sizes.
9. Assign each student a placement band:
      returning students: final promotion score band
      new students with assessment: assessment score band
      new students without assessment: D band
10. Group students by band:
      A: 90-100
      B: 80-89
      C: 70-79
      D: 60-69
11. Sort bands from highest to lowest: A, B, C, D.
12. Within each band, sort students deterministically:
      final/placement score descending when available
      student name ascending or student_id ascending as tie-breaker
13. Assign students one by one to the eligible section with:
      lowest current total count
      then lowest count for that student's score band
      then deterministic section order
14. Do not exceed target section size unless all target sizes are full or admin override exists.
15. Validate every student is assigned exactly once.
16. Save placement snapshot.
17. Show admin preview.
18. Finalize only after preview confirmation.
```

### Tie-break order

The section assignment tie-break order must be:

```text
lowest total section count
then lowest count in the student's score band
then deterministic section order
```

This order matters.

If the system prioritizes band equality before total section size, totals can drift. If it prioritizes total size first and band count second, it produces more balanced sections.

---

## 15. Example Balanced Distribution

For 32 eligible students and capacity 20:

```text
section_count = ceil(32 / 20) = 2
target size = 16 and 16
```

A valid balanced result:

| Band | Section A | Section B |
|---|---:|---:|
| 90-100 | 4 | 4 |
| 80-89 | 5 | 5 |
| 70-79 | 4 | 5 |
| 60-69 | 3 | 2 |
| **Total** | **16** | **16** |

This is the desired behavior: both sections have equal total size, and the band mix is as balanced as possible.

---

## 16. Placement Preview Before Finalization

The system must not immediately write final class assignments.

It should first create a draft placement batch.

### Preview should show

```text
section count
capacity used
target section sizes
student count per section
score-band distribution per section
new students defaulted to D band
students excluded from placement
students needing exception review
capacity warnings
duplicate placement warnings
```

Admin should then finalize the preview.

Only after finalization should the system write final class/section assignments.

This avoids the classic software maneuver where a draft becomes reality because somebody clicked the wrong button and the database had no spine.

---

## 17. Placement Snapshot and Audit Tables

Create a placement batch snapshot so the school can explain how students were assigned.

### Recommended table: `section_placement_batches`

```text
id
from_school_year
to_school_year
from_grade_level_id
to_grade_level_id
section_capacity_used
total_students
section_count
algorithm
configuration_snapshot_json
created_by
created_at
finalized_by
finalized_at
status
```

Recommended statuses:

```text
draft
finalized
cancelled
superseded
```

### Recommended table: `section_placement_batch_students`

```text
id
batch_id
student_id
student_type
source_decision_id nullable
source_enrollment_id nullable
final_score nullable
placement_score nullable
score_band
placement_band_reason
assigned_section_id
assignment_order
was_override
override_reason
created_at
```

### Placement band reasons

```text
returning_student_final_score
new_student_assessment
new_student_default_band
admin_override
```

---

## 18. Validation Before Placement Generation

Before generating placement, validate:

```text
target school year exists
target school year is open for enrollment/placement
target grade/class exists
section_capacity config exists or fallback is applied
source class/year is finalized where applicable
`student_decisions` exists for returning students
returning students have passed/promoted decisions
returning students completed parent enrollment
new students are admin-approved
students are not already placed in target year
target grade/class matches the promotion path
```

---

## 19. Validation Before Placement Finalization

Before finalizing placement, validate:

```text
all eligible students are assigned exactly once
no ineligible student is assigned
no student is already assigned to another target-year section
all sections belong to the target year and grade/class
no section exceeds target size unless override is recorded
score-band distribution report was generated
section_capacity_used is stored
placement snapshot is saved
admin finalized the preview
```

---

## 20. Edge Cases

| Case | Rule |
|---|---|
| Score exactly 90 | Band A |
| Score exactly 80 | Band B |
| Score exactly 70 | Band C |
| Score exactly 60 | Band D |
| Score below 60 | Not automatic promotion unless override |
| Missing final score for returning student | Exception report |
| Missing `student_decisions` | Exception report |
| Pending `student_decisions` | Exception report |
| Parent did not complete returning enrollment | Do not place |
| New student without assessment | D band for balancing only |
| New student with assessment score | Band from assessment |
| Duplicate target-year placement | Block and report |
| Capacity exceeded | Admin override required |
| Uneven score bands | Balance total section size first, band mix second |
| Config changes after placement | Old placement uses stored `section_capacity_used` |

---

## 21. Required Reports

The admin should have reports for:

```text
Passed but parent enrollment missing
Ready for placement
New students approved and ready for placement
Missing/pending student decisions
Failed/retained students
Exception review required
Draft placement batches
Finalized placement batches
Capacity and section distribution
New students defaulted to D band
Manual overrides
```

---

## 22. Service Design

Recommended services:

```text
StudentDecisionEligibilityService
ReturningEnrollmentStatusService
NewStudentAdmissionStatusService
PlacementPoolBuilder
PromotionSectionCapacityService
ScoreBandClassifier
BalancedSectionPlacementService
SectionPlacementPreviewService
SectionPlacementFinalizationService
SectionPlacementAuditService
```

### Responsibility split

| Service | Responsibility |
|---|---|
| `StudentDecisionEligibilityService` | Reads `student_decisions`; determines pass/fail eligibility |
| `ReturningEnrollmentStatusService` | Checks parent returning enrollment completion |
| `NewStudentAdmissionStatusService` | Checks admin-approved new students |
| `PlacementPoolBuilder` | Builds final placement pool |
| `PromotionSectionCapacityService` | Reads and validates `promotion.section_capacity` |
| `ScoreBandClassifier` | Assigns A/B/C/D bands |
| `BalancedSectionPlacementService` | Generates balanced section assignments |
| `SectionPlacementPreviewService` | Creates draft placement preview |
| `SectionPlacementFinalizationService` | Writes final class assignments |
| `SectionPlacementAuditService` | Stores snapshots and reports |

Controllers should orchestrate requests, not contain placement policy. Controllers are traffic cops, not the Ministry of Education.

---

## 23. Implementation Priority

### Phase 1: Audit and preserve current data

```text
Document current tables and flows.
Add lifecycle/display metadata where needed.
Do not silently migrate old rows.
Keep old records displayable.
```

### Phase 2: Strengthen pass/fail eligibility

```text
Use `student_decisions` as source of truth.
Add reports for missing/pending decisions.
Block automatic placement without passed/promoted decision.
```

### Phase 3: Strengthen returning enrollment

```text
Require parent completed returning enrollment.
Separate returning enrollment from new-student registration.
Admin approval only for new students and exceptions.
```

### Phase 4: Add configurable section capacity

```text
Add/use `promotion.section_capacity` in configuration table.
Validate it.
Default to 20 only if missing.
Store capacity used in placement snapshot.
```

### Phase 5: Build placement pool

```text
Merge eligible returning students and approved new students.
Default new students without assessment to D band.
Exclude ineligible and incomplete records.
Generate exception report.
```

### Phase 6: Generate balanced placement preview

```text
Create sections based on capacity.
Group students by score band.
Balance section totals first, band mix second.
Generate draft preview.
```

### Phase 7: Finalize placement

```text
Validate draft.
Admin finalizes.
Write class section assignments.
Store placement snapshot.
```

### Phase 8: Add audit and reports

```text
Placement batch history.
Distribution reports.
Override logs.
New-student D-band report.
Parent enrollment missing report.
```

---

## 24. Final Policy Summary

The strong system must follow these rules:

```text
1. New students require admin approval.
2. Returning students do not require admin approval unless there is an exception.
3. Returning students must pass according to `student_decisions`.
4. Returning students must have parent enrollment completed.
5. Promotion placement must not recalculate pass/fail.
6. Section capacity comes from the `configuration` table using `promotion.section_capacity`.
7. If total placement pool size is less than or equal to capacity, create one section.
8. If total placement pool size is greater than capacity, create ceil(total / capacity) sections.
9. Returning students are grouped by final promotion score bands.
10. New students without assessment are placed in D band for balancing only.
11. Multiple sections must be balanced by total section size first and score-band mix second.
12. Placement must be previewed before finalization.
13. Final placement must store a snapshot of the algorithm, capacity, distribution, and assigned students.
14. Old data remains displayable and must not be silently changed.
```

---

## 25. Bottom Line

The strong model is:

```text
student_decisions = academic eligibility
parent returning enrollment = family intent to continue
admin approval = new-student admission or exception handling
configuration = section capacity
placement algorithm = balanced section assignment
snapshot = audit trail
```

That separation is the whole point.

Without it, the system will keep confusing pass/fail, enrollment, and placement. Then someone will ask why a failed student is in the next class, why a parent never enrolled but the child appears in attendance, or why one section has all the high scorers. And then everyone will stare at the database like it committed a moral crime on its own.

It did not. The design did.
