Skip to main content

qualia_client_core/wellfair/
scorecard_prefs.rs

1//! **The person's own score-card weight model** — their authorship of *how their body is read*.
2//!
3//! The `WeightModel` is the interpretive lens the score-card uses (which systems carry "stress", which carry
4//! "resilience", …). The software must not *define* the person through a fixed lens; it offers a **seed
5//! suggestion** the person can adopt, edit, or replace, and stores **their** model here. Absence of a stored
6//! model means "the person has not authored one yet" — the caller falls back to the seed *suggestion*, never
7//! an imposed definition. Forum-internum / Sanctuary-class selfhood config; the person's alone.
8
9use std::path::Path;
10
11use wellfare_core::anatomy::WeightModel;
12
13/// Where the person's authored weight model lives (prefs-style, under the `wellfair/` prefix).
14pub const WEIGHT_MODEL_FILE: &str = "wellfair/weight_model.json";
15
16/// Load the person's **authored** weight model, or `None` if they have not authored one (→ the caller uses
17/// the seed *suggestion*).
18pub fn load(storage_root: impl AsRef<Path>) -> Option<WeightModel> {
19    let path = storage_root.as_ref().join(WEIGHT_MODEL_FILE);
20    let bytes = std::fs::read(path).ok()?;
21    serde_json::from_slice(&bytes).ok()
22}
23
24/// Persist the person's own weight model — their authorship of the interpretation.
25pub fn save(storage_root: impl AsRef<Path>, model: &WeightModel) -> Result<(), String> {
26    let path = storage_root.as_ref().join(WEIGHT_MODEL_FILE);
27    if let Some(parent) = path.parent() {
28        std::fs::create_dir_all(parent).map_err(|e| e.to_string())?;
29    }
30    let bytes = serde_json::to_vec_pretty(model).map_err(|e| e.to_string())?;
31    std::fs::write(path, bytes).map_err(|e| e.to_string())
32}
33
34/// Clear the person's authored model — revert to the seed *suggestion*. Idempotent (no-op if none exists).
35pub fn clear(storage_root: impl AsRef<Path>) -> Result<(), String> {
36    let path = storage_root.as_ref().join(WEIGHT_MODEL_FILE);
37    match std::fs::remove_file(path) {
38        Ok(()) => Ok(()),
39        Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(()),
40        Err(e) => Err(e.to_string()),
41    }
42}
43
44#[cfg(test)]
45mod tests {
46    use super::*;
47
48    #[test]
49    fn authored_model_persists_and_clears_back_to_none() {
50        let dir = tempfile::tempdir().unwrap();
51        // Nothing authored yet → None (caller uses the seed suggestion).
52        assert!(load(dir.path()).is_none());
53
54        // The person authors their own model.
55        let mut model = wellfare_core::anatomy::seed_weight_model();
56        model
57            .system_weights
58            .push(wellfare_core::anatomy::SystemAspectWeight {
59                system_id: "nervous".into(),
60                aspect: wellfare_core::anatomy::Aspect::Stress,
61                weight_pct: 42,
62            });
63        save(dir.path(), &model).unwrap();
64        assert_eq!(
65            load(dir.path()).as_ref(),
66            Some(&model),
67            "the person's model is theirs, persisted"
68        );
69
70        // They can revert to the suggestion.
71        clear(dir.path()).unwrap();
72        assert!(
73            load(dir.path()).is_none(),
74            "reset returns to the seed suggestion"
75        );
76    }
77}