Skip to Content

Intervention Fidelity Tracker

The fidelity tracker (WP-FID-BE) answers one question before any tier escalation: was the plan actually delivered? It computes a fidelity rate (sessions completed ÷ planned) for an active bundle assignment, maps it to a rating, cross-references the growth signal from progress monitoring, and returns a verdict plus an escalationBlocked flag. The RTI engine reads this gate inline before any Tier 2 → Tier 3 move.

The pedagogical point: a plan that did not work is different from a plan that was not done. Faithful delivery (≥80%) with weak growth MAY escalate (the plan was tried and did not work) — but ≥80% alone is no longer sufficient (see Off-track lifecycle below): a major_fidelity_issue_flag (very short sessions / incomplete materials / mis-delivery) forces the result not-yet-decidable even at ≥0.80. Under-80% with weak growth is BLOCKED, because you cannot indict a plan that was never run. No session data returns cannot_evaluate_yet, never a guessed pass. In every “not decidable” case the gap is in delivery, never student non-response.

Routes

MethodPathPermission
GET/api/fidelity/{bundleAssignmentId}/checkVIEW_OWN_CLASSES (ClassTeacher) or RUN_BACKOFFICE
GET/api/fidelity/{bundleAssignmentId}VIEW_OWN_CLASSES (ClassTeacher) or RUN_BACKOFFICE
POST/api/fidelity/{bundleAssignmentId}/recomputeVIEW_OWN_CLASSES (ClassTeacher) or RUN_BACKOFFICE
POST/api/fidelity/{bundleAssignmentId}/sessionVIEW_OWN_CLASSES (ClassTeacher) or RUN_BACKOFFICE
GET/api/students/{id}/fidelity-trackerVIEW_OWN_CLASSES (ClassTeacher) or RUN_BACKOFFICE
GET/api/fidelity/admin/low-fidelity-cohortRUN_BACKOFFICE (admin only)

bundleAssignmentId and student id are positive integers. The teacher routes gate on the student’s class through the in-handler ClassTeacher guard; a cross-org or out-of-scope id returns 404 (existence non-disclosure). There is deliberately no student endpoint; students never see fidelity.

POST /api/fidelity/{bundleAssignmentId}/session is the primary write path: it logs one delivered (or scheduled-but-not-delivered) session, triggers a recompute, and returns a fresh projection. See the Log Intervention Session guide for the request body fields.

GET /check: the escalation gate

This is the synchronous verdict the RTI engine calls inline before an escalation. It returns the latest persisted projection; it does not recompute.

curl https://localhost:3000/api/fidelity/8124/check \ -H "Authorization: Bearer <accessToken>"
{ "bundleAssignmentId": 8124, "fidelityRate": 0.86, "fidelityRating": "high", "fidelityIssueFlag": false, "fidelityIssueCode": null, "impactOnDecision": "no_issue", "growthSignal": "low_growth", "verdict": "adjust_intervention", "escalationBlocked": false, "recommendedAction": "...", "autoDraftedParagraphAr": "...", "sessionsCompleted": 12, "sessionsPlanned": 14 }

The five-value fidelityRating is high (≥0.80) / adequate (0.60 to 0.79) / partial (0.40 to 0.59) / low (below 0.40) / unknown (no data). The four-value verdict is continue / adjust_intervention / cannot_evaluate_yet / fortuitous_complete_dose. impactOnDecision is block_escalation / reduce_confidence / no_issue. The growthSignal (good_growth / partial_growth / low_growth / insufficient_data) is read from the monitoring module.

The rating × growth matrix

The verdict and escalationBlocked come from a pure matrix of rating against growth:

fidelity \ growthgoodlowhalt (do_not_decide_yet / acute)
high (≥0.80)continue (not blocked)adjust_intervention (not blocked)cannot_evaluate_yet (blocked)
adequate (0.60–0.79)continue (not blocked)cannot_evaluate_yet (blocked)cannot_evaluate_yet (blocked)
partial (0.40–0.59)fortuitous_complete_dose (not blocked)cannot_evaluate_yet (blocked)cannot_evaluate_yet (blocked)
low (below 0.40)fortuitous_complete_dose (not blocked)cannot_evaluate_yet (blocked)cannot_evaluate_yet (blocked)
unknown (no data)cannot_evaluate_yet (blocked)cannot_evaluate_yet (blocked)cannot_evaluate_yet (blocked)

The halt sentinel always returns cannot_evaluate_yet + blocked (the Tie Rule). Escalation is permitted only at high or adequate fidelity with a growth signal that the matrix lets through.

Off-track lifecycle and effect validity (R12, FR-FID-13)

R12 (FR-FID-13, spec 35-fidelity-tracker.md) adds a delivery-continuity lifecycle that layers on top of the rate matrix. It answers a second question the rate alone cannot: has the plan stalled in delivery? Two new columns capture it.

intervention_track_status{on_track, partial_delivery, off_track, resumed_pending_new_evidence} — the delivery-continuity lifecycle. A plan goes off_track when NO planned session was delivered for offTrackThresholdWeeks consecutive instructional weeks (config-driven via InterventionTrackingConfig.offTrackThresholdWeeks, default 3). An instructional week is an ISO week that had ≥1 planned session and was not a holiday week; holidays and weeks with no planned session do NOT count toward the consecutive total. partial_delivery = some planned weeks were missed but below the off-track threshold; resumed_pending_new_evidence = the teacher resumed an off-track plan and fresh comparable evidence has not yet accrued.

intervention_effect_validity_status{valid, limited, invalid_due_to_implementation_gap} — whether the student’s response may be read as a real plan effect:

  • invalid_due_to_implementation_gap — low/unknown fidelity, OR a major_fidelity_issue_flag, OR off_track. The gap is in delivery, never student non-response. ≥80% alone is not sufficient: a major fidelity issue (very short sessions / incomplete materials / mis-delivery) forces invalid even at ≥0.80.
  • limitedpartial fidelity or a resumed plan: interpret with caution, never causal.
  • validadequate/high fidelity, no major issue, and on-track.

Off-track BLOCKS all automatic tier movement. When off_track, the recompute FORCES escalationBlocked = true even when the rate matrix would not block, so the RTI engine’s TM_BLOCK_01 gate bars ALL automatic tier movement — escalation, de-escalation, AND graduation to lighter support — until the teacher resumes the plan. A tier cannot move while blocked. The numeric difference between assessments may still be shown as a performance result, but is never attributed to the intervention. The off-track signal reaches the RTI engine through the existing fidelity-reader seam (an offTrack boolean on the reading) — no new RTI binding.

Resume. On resume the teacher selects a controlled reason code from the closed ADD_Context_Flag_Dictionary (B.24 — no free text); the row records resumeReasonCode + resumedAt and sets intervention_track_status = resumed_pending_new_evidence. Effect validity stays limited until fresh comparable points accrue.

POST /recompute: append a fresh snapshot

Recompute forces a fresh computation and appends a new immutable snapshot row. It carries no body: there is no free-text field on any fidelity path; the only issue context is the controlled fidelityIssueCode vocabulary (missed_sessions / short_duration / wrong_grouping / incomplete_material / unknown).

curl -X POST https://localhost:3000/api/fidelity/8124/recompute \ -H "Authorization: Bearer <accessToken>"

The recompute is advisory-locked per (org, assignment) and idempotent on unchanged inputs. It returns the fresh projection (the same shape as /check) with a 201.

The auto-drafted paragraph

Each verdict auto-drafts a short Arabic review-meeting paragraph (autoDraftedParagraphAr). It is rule-written, never an LLM, and is filtered through the language-safety layer for clinical, framework, and deficit language. It is a teacher/admin surface only.

Invariants

  • Fail-closed. A missing assignment is a 404; a missing growth read or no snapshot row yields unknown / escalationBlocked = true, never a fabricated adequate. Absence of fidelity data blocks escalation; it never allows it.
  • Append-only (V-6). intervention_fidelity and intervention_session_log are pure audit tables: no update, no delete. The config version is pinned on every row, so a recompute of the same inputs replays byte-for-byte.
  • No free text. No reason / note / comment body field exists on any path; issue context is the controlled fidelityIssueCode vocabulary.
  • No LLM (V-5). The verdict and the paragraph are deterministic and rule-written.
  • Tenant-isolated. organizationId scopes every read and write; cross-org returns 404.
  • Teacher-scoped (ClassTeacher) / admin only; never student-facing.