Standards & Benchmarks
The standards module (WP-STD-BE, modules/standards) turns a raw measurement score into a MeasureStatus verdict by comparing it against partner-owned, country-keyed, versioned cut scores. It also manages those cut scores without requiring code changes.
There is no single overall score or percentage anywhere in the system (V-3). Every status is per-measure or per-macro-domain. Not_Assessed is never treated as zero.
MeasureStatus: the 5-value verdict
A measurement score resolves to exactly one of five statuses. These are surfaced as descriptive Arabic labels, never as numbers or percentages.
| Status key | Meaning |
|---|---|
meets | Score meets the target cut |
approaching | Score is above the lower threshold but below the target |
below | Score is below the lower threshold |
severe | Score is zero, or the student was unable to read the first word (partner-defined zero-rule) |
not_assessed | Score is null, no numeric cuts are configured, or the partner has not yet supplied cut scores for this dimension |
not_assessed is excluded from any rollup. It is never averaged, never treated as a low score.
Three-step resolution chain
For a given (country, skillId, assessmentType, gradeBand) the engine walks three levels:
- Exact match: a profile for this exact country + skill + assessment type + grade band.
- Country default: if no exact match, a profile for the same country with
skillIdnull. - Global fallback: if still no match, a profile with
countrynull (the__GLOBAL__sentinel). This row is always present, so resolution never returns null.
Fallback steps 2 and 3 write a resolution_fallback audit row. If all three steps fail (not possible with the shipped seed but defensible in future) a resolution_miss is logged and the status is not_assessed.
Session pinning (V-6)
When a session begins, freezeBenchmarkResolution writes the resolved { profileId, version } map once for that (org, session) pair. All score evaluations within the session use that pinned version. A mid-session admin benchmark swap does not affect in-flight sessions. Each probe row permanently stamps benchmarkProfileId and benchmarkProfileVersion for replay.
Versioning and atomic swap
Benchmark profiles are versioned. The admin activate endpoint performs an atomic swap under a Postgres advisory lock: in a single transaction it deactivates the current active profile, activates the new one, and appends a changelog row. A partial-active uniqueness constraint at the database level provides a second safety belt.
The change-log records every create, activate, deactivate, resolution_fallback, and resolution_miss event, with the admin user ID and version transition.
Seeded cut scores
Real partner cut scores for Jordan (JO) and Palestine (PS) are seeded at setup time. These numbers come from the partner workbook and are never hand-edited in documentation or code. If the partner updates the workbook, numbers are re-extracted from it and a new benchmark profile is created and activated through the admin API. No code change is needed.
__GLOBAL__ rows carry working-default values (valuesPending: true) until the partner supplies final numbers for that dimension.
No framework or clinical labels on any surface
The internal computation layer uses source identifiers (such as JOR_RAMP, plus a Palestine equivalent) drawn from the partner workbook. A serialization-layer guard (strip-yardstick.ts) strips these identifiers before any teacher or admin response leaves the server. The resolve endpoint omits the source field entirely. These labels never appear in any user-facing surface.
Principal school overview
The principal dashboard reads from the standards module for two surfaces:
- School Literacy Health overview: Curriculum-KPI gauges displayed with Arabic labels and status categories.
not_assessedis excluded from the school health index; it is shown separately, never folded into the status distribution. - Cross-skill heatmap: per-skill status counts across the school’s students.
Neither surface displays a number or percentage representing overall literacy. See Managers & Principals for how principals and managers read these views.
API reference
| Method | Path | Permission | Notes |
|---|---|---|---|
GET | /api/standards/benchmark-profiles/resolve | Teacher / admin | Resolve the active profile for a given context |
GET | /api/admin/benchmark-profiles | RUN_BACKOFFICE | List all profiles with version and activation state |
POST | /api/admin/benchmark-profiles | RUN_BACKOFFICE | Create a new profile (optionally activate immediately) |
POST | /api/admin/benchmark-profiles/:id/activate | RUN_BACKOFFICE | Atomic swap-activate |
GET | /api/admin/benchmark-profile-changelog | RUN_BACKOFFICE | Full audit log of all profile events |
GET | /api/principal/schools/:id/overview | VIEW_SCHOOL | School health gauges |
GET | /api/principal/schools/:id/heatmap | VIEW_SCHOOL | Cross-skill status heatmap |
POST | /api/principal/schools/:id/ministry-report | VIEW_SCHOOL | Wave 1 stub (returns 501; Wave 2 feature) |
No endpoint grants a student permission. Benchmark data is never served directly to students or parents.
Related
- Skill-Status Engine: consumes
MeasureStatusto derive skill-level and domain-level status - Reporting & Parent PDF: surfaces status categories to teachers and parents
- Managers & Principals: how the principal dashboard reads the school overview and heatmap
- Glossary:
MeasureStatus,delta_prior, calibration