Result Remediation Roadmap (Phase 1–2 implemented)
Scope: Phase-based (no dates). Phase 1–2 backend foundations are implemented; Phase 3+ (preview/approvals/interventions) is deferred.
0. Current State (Baseline)
Already implemented and proven via backend/tests scripts:
-
Targeted item-level remediation
mark_correcton single and multi-item submissions.drop_itemto remove flawed items from scoring.replace_keyto swap correct options.
-
Scoped impact
- Only submissions containing the targeted
(evaluationVersionId, questionVersionId)are rescored.
- Only submissions containing the targeted
-
Multi-attempt support
- Same user, multiple attempts; remediation batch rescoring all relevant attempts.
-
Non-destructive behaviour
- Version snapshots and raw answers remain unchanged; only score/interpretation changes.
These are the engine primitives that all later phases build on.
Phase 1 – Domain & Audit Foundations (Implemented)
Goal: Turn remediation from “just some code” into a first-class, auditable domain concept without yet adding complex workflow.
1.1 Harden CorrectionBatch as a domain entity
Make CorrectionBatch the central audit unit for remediation.
-
Add/ensure fields:
-
id -
tenant_id(andorg_unit_idif applicable) – RLS scope -
status(enum): initiallydraftandapplied- Explicitly leave room for
pending_review,approved,revertedlater.
- Explicitly leave room for
-
reason(non-null, “why we did this”) -
created_by_user_id -
created_at -
applied_at -
correction_payload(existing JSON of corrections)
-
Outcome: Every remediation action has a WHO / WHEN / WHY and a clear status, even with a simple draft→applied lifecycle.
1.2 Add score history / remediation log
Introduce a dedicated history structure so we can reconstruct score changes over time and support safe reversal.
-
New table (name flexible, e.g.
submission_score_history):idsubmission_idcorrection_batch_id(NULL for original scoring)scoremax_scorecreated_at
-
Invariants:
- Initial scoring writes a row with
correction_batch_id = NULL. - Every remediation apply writes a new row per affected submission with the batch id.
submissions.scoreandsubmissions.max_scoreare always the latest history row.
- Initial scoring writes a row with
Outcome: We can always explain:
- What a submission’s score is now,
- How it got there,
- Which batch changed it (if any).
This table underpins both auditability and reversal.
1.3 Engine integration & idempotency
Wire the existing remediation engine to:
-
Always:
- Record history rows.
- Update
submissions.score/max_scoreatomically.
-
Ensure idempotency:
- Re-applying the same batch does not keep boosting scores.
- Existing idempotent behaviour from scripts is preserved and enforced via tests.
Outcome: The engine is safe to call multiple times and leaves a clean history trail.
Phase 2 – Basic Workflow Services & Reversal (Implemented, stop here)
Goal: Expose remediation as a simple, safe workflow that frontend can use, including reversal. This is the initial implementation boundary – development stops here for now.
2.1 Minimal batch lifecycle APIs
Expose a thin but coherent API so admins can see and apply batches.
-
Create batch (draft)
POST /results/correction-batches- Creates a
draftbatch withreason,created_by_user_id,created_at,correction_payload.
-
List batches
GET /results/correction-batches?status=draft|applied- Returns WHO/WHEN/WHY plus simple metadata (e.g. type of corrections).
-
Apply batch
-
POST /results/correction-batches/\{id\}/apply -
Preconditions:
- Batch is
draft.
- Batch is
-
Effects:
- Runs remediation engine, writes score history.
- Updates
submissions.score/max_score. - Sets
status = "applied",applied_at = now().
-
(Optional but nice now or later: a lightweight GET /results/correction-batches/\{id\} for details.)
Delivered: list/detail/apply/revert endpoints with status/appliedAt and history-based revert.
2.2 Reversal / undo (via compensating batch)
Deliver a reversal mechanism as part of this phase – this is considered essential.
-
Design a reversal flow using history:
-
For a given
correction_batch_id, usesubmission_score_historyto:- Determine the previous score/max_score per submission (pre-batch state).
-
Create a new
CorrectionBatchthat:- Restores those previous scores (or logically cancels the effects).
- Writes new history rows with a reference to the original batch in the reason or a dedicated field.
-
-
API:
-
POST /results/correction-batches/\{id\}/revert- Creates and applies a new compensating batch.
- Does not delete or mutate the original batch/history.
-
Key point: Reversal is additive, not destructive. Audit trail stays append-only.
2.3 Simple workflow contract for frontend
For the frontend team, the contract after Phase 2 is:
-
They can:
- List existing remediation batches.
- Drill into a batch to see its payload and meta (reason, who, when, status).
- Apply a batch once they’re satisfied.
- Revert a batch if needed (which internally creates a compensating batch).
-
They should not yet expect:
- Tag-based batch creation helpers.
- Preview of “who will pass/fail” before applying.
- Multi-step approvals or an “Action Center” UI.
Initial Development Stop: Implementation for remediation stops at the end of Phase 2:
- Domain & audit foundations (Phase 1) ✅
- Basic lifecycle APIs + reversal (Phase 2) ✅
- No further remediation features are built until a future phase.
Phase 3+ (Deferred)
- Impact preview (who will pass/fail).
- Approvals/dual control and intervention workflows.
- Batch creation helpers (tag-based/bulk).
Phase 3 – Preview & Impact Analysis (LATER)
Goal: Allow admins to see the impact of a batch before confirming.
3.1 Dry-run scoring & submission-level impact
-
GET /results/correction-batches/\{id\}/preview:-
Runs the remediation engine in dry-run mode.
-
Returns:
-
submissionsAffected -
counts of:
passToFailfailToPassoutcomeUnchanged
-
-
3.2 Assignment/programme impact
Extend preview to include assignment-level deltas:
-
For each assignment/programme impacted:
-
List users who will:
- move from pass → fail,
- move from fail → pass,
- stay the same.
-
This enables UX like:
“X submissions will be updated These 5 users will then FAIL the assignment ‹Final Safety Exam› These 2 users will then PASS…”
Frontend can decide how much detail to show (on-screen vs CSV export).
Phase 4 – Advanced Governance & Approvals (LATER)
Goal: Add enterprise-grade governance for high-stakes environments.
Possible extensions:
-
Batch status workflow:
draft → pending_review → approved → applied → reverted.
-
Dual-control:
- Creator and approver must be different users.
-
Action Center UI:
- Central place to see all batches, statuses, and key metrics.
-
Finer-grained access control:
- Only users with specific capabilities can create, approve, apply, or revert batches.
All of this builds directly on top of Phase 1–3 foundations.
Phase 5 – Intervention & Controlled Retakes (LATER)
Goal: Tie remediation into assignment/programme lifecycle.
features (later):
-
Flags or statuses such as
requires_intervention_before_retake. -
Enforcement in
create_sessionso:- learners can’t retake until an instructor reviews,
- or a specific remediation step is completed.
-
Programme-level hooks:
- Automatically issue remedial assignments based on outcomes.
These depend on existing assignment/programme models and the stable remediation core from Phases 1–2.
Summary
-
Now (Initial Implementation):
- Phase 1 – Harden
CorrectionBatch+ add score history. - Phase 2 – Expose basic workflow services and implement a non-destructive reversal mechanism. → Development stops here for the first remediation release.
- Phase 1 – Harden
-
Later:
- Phase 3 – Preview & impact analysis.
- Phase 4 – Governance, approvals, “Action Center”.
- Phase 5 – Intervention & controlled retakes.
This keeps the backend robust and future-proof, gives the frontend team clear, usable primitives, and leaves plenty of room to grow into a full-blown, enterprise-ready remediation experience when the time is right.