mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
fix(insights): close the deterministic score assessment with one grounded step
The per-score deterministic text named the score, its standing, and the weakest contributor, then stopped at the diagnosis. When the band is not green and the weakest driver is behaviourally addressable (sleep, mood, consistency, timing), append a single grounded pointer drawn from that same contributor. Physiology-only drivers add nothing, so the text affirms and watches rather than manufacturing a step.
This commit is contained in:
1 parent
b470a14187
commit
5b406d58cd
2 files changed
+75
No files matched your search
@@ -158,6 +158,41 @@ describe("resolveDeterministicAssessment", () => {
|
||||
expect(a!.text).toContain("Schlafmenge");
|
||||
});
|
||||
|
||||
it("appends a grounded next step when the weakest contributor is addressable (en)", () => {
|
||||
// sleep (30) is the lowest present contributor → a behaviourally
|
||||
// addressable driver, so the assessment closes with a doable pointer.
|
||||
const sleepWeak: ReadinessValue = {
|
||||
score: 58,
|
||||
band: "yellow",
|
||||
components: [
|
||||
{ key: "rhr", value: 80, weight: 0.3 },
|
||||
{ key: "hrv", value: 75, weight: 0.3 },
|
||||
{ key: "sleep", value: 30, weight: 0.25 },
|
||||
{ key: "mood", value: 70, weight: 0.15 },
|
||||
],
|
||||
};
|
||||
const a = resolveDeterministicAssessment(
|
||||
"READINESS",
|
||||
okDerived(sleepWeak),
|
||||
"en",
|
||||
NOW,
|
||||
);
|
||||
expect(a!.text).toContain("Held back most by");
|
||||
expect(a!.text).toContain("An earlier night would lift this most.");
|
||||
});
|
||||
|
||||
it("does NOT manufacture a step when the weakest contributor is physiology-only (en)", () => {
|
||||
// rhr (40) is the lowest in the base readiness fixture → no pointer.
|
||||
const a = resolveDeterministicAssessment(
|
||||
"READINESS",
|
||||
okDerived(readiness),
|
||||
"en",
|
||||
NOW,
|
||||
);
|
||||
expect(a!.text).not.toContain("An earlier night");
|
||||
expect(a!.text).not.toContain("most effective lever");
|
||||
});
|
||||
|
||||
it("uses the trend for a contributor-less recovery score (en)", () => {
|
||||
const a = resolveDeterministicAssessment(
|
||||
"RECOVERY_SCORE",
|
||||
|
||||
@@ -247,6 +247,41 @@ function contributorLabel(
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* v1.21.0 (coach C1 MEDIUM-2) — a single grounded next-step pointer keyed to
|
||||
* the weakest BEHAVIOURALLY ADDRESSABLE contributor. Only the contributors a
|
||||
* person can actually move (sleep, mood, consistency, timing) carry a
|
||||
* pointer; physiology-only contributors (rhr, hrv, respiratory) return null,
|
||||
* so the assessment affirms-and-watches rather than manufacturing a step —
|
||||
* matching the `base-system.ts` "do NOT manufacture a step" rule.
|
||||
*/
|
||||
const CONTRIBUTOR_POINTERS: Record<string, { de: string; en: string }> = {
|
||||
sleep: {
|
||||
de: "Eine etwas frühere Nacht würde das am ehesten anheben.",
|
||||
en: "An earlier night would lift this most.",
|
||||
},
|
||||
sufficiency: {
|
||||
de: "Etwas mehr Schlafzeit ist hier der wirksamste Hebel.",
|
||||
en: "A little more time asleep is the most effective lever here.",
|
||||
},
|
||||
consistency: {
|
||||
de: "Gleichmäßigere Schlafzeiten über die Woche helfen am meisten.",
|
||||
en: "More even sleep and wake times across the week help most.",
|
||||
},
|
||||
timing: {
|
||||
de: "Ein gleichmäßigerer Rhythmus zieht das am ehesten nach oben.",
|
||||
en: "A steadier sleep rhythm is the most likely thing to pull this up.",
|
||||
},
|
||||
mood: {
|
||||
de: "Ein kurzer Moment für etwas, das dir guttut, kann hier spürbar helfen.",
|
||||
en: "A small moment for something that does you good can noticeably help here.",
|
||||
},
|
||||
};
|
||||
|
||||
function contributorPointer(key: string, locale: Locale): string | null {
|
||||
return CONTRIBUTOR_POINTERS[key]?.[locale] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose the deterministic per-score assessment from the score's signal +
|
||||
* its contributors. Always returns a non-empty, factual, non-causal text.
|
||||
@@ -294,6 +329,11 @@ export function buildDeterministicScoreAssessment(
|
||||
? `Am stärksten gedämpft durch ${joined}.`
|
||||
: `Held back most by ${joined}.`,
|
||||
);
|
||||
// v1.21.0 (MEDIUM-2) — close with ONE grounded next step drawn from the
|
||||
// weakest contributor, but only when it is behaviourally addressable.
|
||||
// When the weakest driver is physiology-only, no step is manufactured.
|
||||
const pointer = contributorPointer(ranked[0].key, locale);
|
||||
if (pointer) sentences.push(pointer);
|
||||
}
|
||||
} else if (signal.delta !== null && signal.delta !== 0) {
|
||||
// No contributor breakdown (recovery/stress/strain): use the trend.
|
||||
|
||||
Reference in new issue
Block a user