Skip to Content

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 keyMeaning
meetsScore meets the target cut
approachingScore is above the lower threshold but below the target
belowScore is below the lower threshold
severeScore is zero, or the student was unable to read the first word (partner-defined zero-rule)
not_assessedScore 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:

  1. Exact match: a profile for this exact country + skill + assessment type + grade band.
  2. Country default: if no exact match, a profile for the same country with skillId null.
  3. Global fallback: if still no match, a profile with country null (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_assessed is 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

MethodPathPermissionNotes
GET/api/standards/benchmark-profiles/resolveTeacher / adminResolve the active profile for a given context
GET/api/admin/benchmark-profilesRUN_BACKOFFICEList all profiles with version and activation state
POST/api/admin/benchmark-profilesRUN_BACKOFFICECreate a new profile (optionally activate immediately)
POST/api/admin/benchmark-profiles/:id/activateRUN_BACKOFFICEAtomic swap-activate
GET/api/admin/benchmark-profile-changelogRUN_BACKOFFICEFull audit log of all profile events
GET/api/principal/schools/:id/overviewVIEW_SCHOOLSchool health gauges
GET/api/principal/schools/:id/heatmapVIEW_SCHOOLCross-skill status heatmap
POST/api/principal/schools/:id/ministry-reportVIEW_SCHOOLWave 1 stub (returns 501; Wave 2 feature)

No endpoint grants a student permission. Benchmark data is never served directly to students or parents.

Last updated on