Merge work/score-coverage-contract

This commit is contained in:
Marc-André Bombeck committed 2026-07-31 20:31:01 +02:00
commit f42d423edd
10 files changed
+360 -7

No files matched your search

@@ -0,0 +1,104 @@
/**
* v1.35.0 — the Health Score's composite-level provenance must not claim
* more than it does.
*
* Two sentences used to overreach, in different ways.
*
* The one people read, `insights.healthScore.method`, said the score was
* the average of "every pillar that has enough recent data". Once somebody
* can take a pillar out of their score, that is simply untrue for them,
* and it is untrue in the direction that matters: it tells them the number
* covers ground it does not. The replacement says the average is over the
* pillars that count, and that which pillars count is theirs to choose.
* This test pins the choice sentence in all six locales, because a
* translation that quietly drops it puts the old claim back for those
* readers only.
*
* The one nobody reads, `METRIC_PROVENANCE.HEALTH_SCORE.standard`, named
* the WHO HEARTS technical package flat, as though a standards body had
* signed off on whatever recipe the account happens to hold. HEARTS names
* the risk factors; it prescribes no average over them. The entry now says
* what it is cited for. It reaches no screen today (`ProvenanceExplainer`
* renders the method caption only), which is exactly why the method copy
* above carries the honesty and this half is a source-of-truth fix.
*
* The marker check is deliberately blunt, and blunt has a failure mode: a
* substring test can pass on prose that means nothing. So it is pinned in
* both directions here, against the wording this release replaced and
* against a rewrite that keeps the meaning, before it is trusted on the
* real bundles.
*/
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { METRIC_PROVENANCE } from "../standards";
const MESSAGES = join(__dirname, "../../../../../messages");
/**
* The phrase in each locale that says the selection belongs to the
* reader. Not a stylistic preference: it is the sentence that stops the
* paragraph from describing a fixed recipe.
*/
const SCOPE_MARKER: Record<string, string> = {
en: "yours to choose",
de: "entscheidest du",
fr: "vous choisissez",
es: "lo eliges tú",
it: "lo scegli tu",
pl: "ty decydujesz",
};
/** The wording this release replaced, kept only to prove the check bites. */
const PRE_V1350_EN =
"The score is the equal-weighted average of every pillar that has enough " +
"recent data, on a 0 to 100 scale.";
/** An honest rewrite that keeps the meaning, to prove the check is quiet. */
const INNOCENT_EN_REWRITE =
"Pillars are averaged with equal weight on a 0 to 100 scale, and which " +
"pillars count is yours to choose.";
function methodCopy(locale: string): string {
const bundle = JSON.parse(
readFileSync(join(MESSAGES, `${locale}.json`), "utf8"),
) as {
insights: { healthScore: { method: string } };
};
return bundle.insights.healthScore.method;
}
function claimsPersonalScope(locale: string, text: string): boolean {
const marker = SCOPE_MARKER[locale];
if (!marker) throw new Error(`no scope marker declared for ${locale}`);
return text.toLocaleLowerCase().includes(marker.toLocaleLowerCase());
}
describe("Health Score provenance claims", () => {
it("bites on the wording this release replaced", () => {
expect(claimsPersonalScope("en", PRE_V1350_EN)).toBe(false);
});
it("stays quiet on a rewrite that keeps the meaning", () => {
expect(claimsPersonalScope("en", INNOCENT_EN_REWRITE)).toBe(true);
});
it.each(Object.keys(SCOPE_MARKER))(
"says in %s that the pillars in scope are the reader's own",
(locale) => {
const copy = methodCopy(locale);
expect(copy.length).toBeGreaterThan(0);
expect(claimsPersonalScope(locale, copy)).toBe(true);
},
);
it("does not cite a standard as describing the composite", () => {
const { name } = METRIC_PROVENANCE.HEALTH_SCORE.standard;
// The bare package title read as an endorsement of the account's own
// recipe. It is cited for the risk factors the pillars are drawn from,
// and the name has to say so.
expect(name).not.toBe("WHO HEARTS technical package");
expect(name).toContain("risk-factor set");
});
});
+21 -1
View File
@@ -12,6 +12,16 @@
*
* Client-safe: a string-keyed metadata map + i18n key names, no compute, no
* server imports — a `"use client"` surface value-imports it freely.
*
* One thing to know before trusting `standard` to reach a reader: it does
* not, everywhere. `ProvenanceExplainer` accepts the prop and renders only
* the method caption (v1.22 folded the cited-standard line out along with
* the ⓘ popover it lived in), so a surface that goes through that component
* shows the method copy and nothing else. `InfoPopover` still renders the
* link, which is why the vitals dashboard does surface citations. The
* Health Score is on the first path, so its honesty about what the number
* covers has to live in `insights.healthScore.method` — the standard entry
* alone would say it to nobody.
*/
import type { ProvenanceStandard } from "./provenance-explainer";
import type { DerivedMetricId } from "@/lib/insights/derived/registry";
@@ -191,7 +201,17 @@ export const METRIC_PROVENANCE: Record<DerivedMetricId, MetricProvenanceMeta> =
HEALTH_SCORE: {
methodKey: "insights.healthScore.method",
standard: {
name: "WHO HEARTS technical package",
// Cited for the risk-factor set the pillars are drawn from, and for
// nothing beyond it. HEARTS names the cardiometabolic factors worth
// following; it does not prescribe an equal-weighted average of
// them, and since v1.35.0 it could not, because which pillars enter
// that average is the account's own selection. Naming the package
// plainly, as this entry used to, reads as a standard endorsing
// whatever recipe the person happens to have chosen, which is a
// claim nobody can make. The pillar-level citations elsewhere in
// this map are unaffected: each of those really does describe the
// band its pillar is graded against.
name: "WHO HEARTS technical package (risk-factor set)",
url: "https://www.who.int/publications/i/item/9789240001367",
},
},