feat: add learning objective targets
Pipeline / check (pull_request) Failing after 3m25s
Pipeline / docs (pull_request) Skipped
Pipeline / nightly (pull_request) Skipped
Pipeline / release (pull_request) Skipped

This commit is contained in:
2026-09-21 14:02:34 -04:00
parent 9755417899
commit 8ec48fb185
32 changed files with 2396 additions and 448 deletions
+38
View File
@@ -9,6 +9,44 @@ use std::fmt;
use serde::{Deserialize, Serialize};
/// Which tier of the objective registry an entry or a reported row belongs to.
///
/// The two words come from the assessment literature and name two different
/// jobs, not two sizes of the same thing. A **learning objective** is what a
/// syllabus lists and what a report classifies as met: the unit a claim is made
/// about. A **learning target** is the specific performance an item is written
/// against and tagged to: what a student aims at in one class, and the evidence
/// a claim about an objective rests on.
///
/// It lives here rather than beside the registry because every layer that
/// reports needs it, and a `bool` named for one of the two tiers leaves the
/// reader guessing which way round it points.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Tier {
/// A learning objective: the tier a mastery claim is made about.
Objective,
/// A learning target: the tier items are tagged to, and evidence for the
/// objective above it.
Target,
}
impl Tier {
/// The snake_case token used in YAML and in flat storage.
pub fn as_str(self) -> &'static str {
match self {
Tier::Objective => "objective",
Tier::Target => "target",
}
}
}
impl fmt::Display for Tier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
/// Cognitive demand, following the revised Bloom taxonomy.
///
/// Serialized as the integers 1 through 5 so YAML reads `level: 3`. The derived