mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 14:16:12 +00:00
Merge branch 'feat/v144-report' into release/v1.44.0
This commit is contained in:
commit
4e71cf4dc2
58 files changed
+5703
-91
No files matched your search
@@ -74,9 +74,9 @@ describe("report scope gating — structural guard", () => {
|
||||
it("reads a plausible catalogue", () => {
|
||||
// Sanity floor: a silently degraded catalogue would pass everything below
|
||||
// vacuously.
|
||||
expect(ALL_LEAF_IDS.length).toBe(104);
|
||||
expect(ALL_LEAF_IDS.length).toBe(106);
|
||||
expect(Object.keys(MEASUREMENT_LEAF_GROUP)).toHaveLength(86);
|
||||
expect(Object.keys(STRUCTURED_LEAF_GROUP)).toHaveLength(18);
|
||||
expect(Object.keys(STRUCTURED_LEAF_GROUP)).toHaveLength(20);
|
||||
});
|
||||
|
||||
it("places every leaf in exactly one group", () => {
|
||||
|
||||
@@ -98,6 +98,8 @@ const LEGIT_IDENTICAL = new Set<string>([
|
||||
"insights.derived.composite.READINESS.component.hrv",
|
||||
"labs.catalog.hs-crp",
|
||||
"measurements.typeHrvRmssd",
|
||||
// The report's SDNN row, named the way the RMSSD row above is.
|
||||
"reportSelection.leafHrvSdnn",
|
||||
"illness.vital.HRV_RMSSD",
|
||||
"settings.sections.sources.metrics.hrvRmssd",
|
||||
"measurements.typeVo2Max",
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* is `STRUCTURED_LEAF_GROUP`, the other is HTML rendered by the real
|
||||
* `<ClinicianView>` over a real fixture. It proves four things per leaf.
|
||||
*
|
||||
* 1. `DISPOSITIONS` is `Record<StructuredLeafId, …>`, so a nineteenth
|
||||
* 1. `DISPOSITIONS` is `Record<StructuredLeafId, …>`, so a new
|
||||
* structured leaf cannot be added to the catalogue without deciding here
|
||||
* whether it renders — and saying so — or `pnpm typecheck` fails. That is
|
||||
* the lock, and it is the compiler's, not this suite's.
|
||||
@@ -291,6 +291,69 @@ const DISPOSITIONS: Record<StructuredLeafId, Disposition> = {
|
||||
},
|
||||
marker: "Semaglutide",
|
||||
},
|
||||
MEDICATION_EFFECT: {
|
||||
kind: "rendered",
|
||||
data: {
|
||||
medicationEffects: [
|
||||
{
|
||||
medicationName: "Effect-marker drug",
|
||||
startsOn: "2026-01-02",
|
||||
targets: [
|
||||
{
|
||||
type: "BLOOD_PRESSURE_SYS",
|
||||
effect: {
|
||||
present: false,
|
||||
reason: "insufficient_after",
|
||||
level: 0.95,
|
||||
window: null,
|
||||
days: {
|
||||
before: 40,
|
||||
after: 3,
|
||||
excludedLowAdherence: 0,
|
||||
excludedNotTaking: 0,
|
||||
},
|
||||
adherence: {
|
||||
status: "tracked",
|
||||
floorPct: 80,
|
||||
lookbackDays: 14,
|
||||
},
|
||||
levelChange: null,
|
||||
slopeChangePerWeek: null,
|
||||
preSlopePerWeek: null,
|
||||
fit: null,
|
||||
confounders: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
marker: "Effect-marker drug",
|
||||
},
|
||||
WORKOUTS: {
|
||||
kind: "rendered",
|
||||
data: {
|
||||
workouts: {
|
||||
sessions: 3,
|
||||
totalDurationSec: 7200,
|
||||
sessionsPerWeek: 0.7,
|
||||
minutesPerWeek: 28,
|
||||
sportCount: 1,
|
||||
bySport: [
|
||||
{
|
||||
sportType: "rowing",
|
||||
sessions: 3,
|
||||
durationSec: 7200,
|
||||
distanceM: 15000,
|
||||
distanceSessions: 3,
|
||||
energyKcal: null,
|
||||
avgHeartRate: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
marker: "15.0 km",
|
||||
},
|
||||
ALLERGIES: {
|
||||
kind: "rendered",
|
||||
data: {
|
||||
@@ -444,7 +507,7 @@ describe("clinician view — every catalogue leaf has a renderer or a reason", (
|
||||
it("reads a plausible catalogue", () => {
|
||||
// Sanity floor: a degraded import would satisfy every loop below
|
||||
// vacuously, which is the failure mode a pair guard dies of.
|
||||
expect(STRUCTURED_LEAF_IDS).toHaveLength(18);
|
||||
expect(STRUCTURED_LEAF_IDS).toHaveLength(20);
|
||||
expect(Object.keys(DISPOSITIONS).sort()).toEqual(
|
||||
[...STRUCTURED_LEAF_IDS].sort(),
|
||||
);
|
||||
|
||||
@@ -30,6 +30,8 @@ const ALLOWED: Record<string, string> = {
|
||||
"The correlation engine's centred sums and the Welch t-test variances.",
|
||||
"lib/insights/mood-tag-influence.ts": "Welch t-test variances.",
|
||||
"lib/insights/mood-pattern-cards.ts": "A pooled variance for a t statistic.",
|
||||
"lib/stats/segmented-regression.ts":
|
||||
"The total sum of squares behind R² and the HAC standard errors of a segmented fit; neither is reported as a spread.",
|
||||
"lib/insights/derived/trajectory.ts":
|
||||
"The residual standard error of a regression fit (÷ n − 2).",
|
||||
"lib/insights/derived/sleep-score.ts":
|
||||
|
||||
@@ -361,6 +361,16 @@ const UNIT_LITERAL_ALLOWLIST: Record<
|
||||
reason:
|
||||
"SI by policy: the clinician artefacts keep mass in kg (glucose follows the owner)",
|
||||
},
|
||||
"lib/doctor-report-pdf/activity-effect-sections.ts": {
|
||||
lines: 2,
|
||||
reason:
|
||||
"SI by policy: the report's workout distance is in km, like every clinician artefact",
|
||||
},
|
||||
"components/clinician/activity-effect-sections.tsx": {
|
||||
lines: 1,
|
||||
reason:
|
||||
"SI by policy: the clinician page's workout distance is in km, like the PDF",
|
||||
},
|
||||
"components/insights/workout-detail/splits.tsx": {
|
||||
lines: 1,
|
||||
reason: "kilometre splits are cut server-side; their pace is per km",
|
||||
|
||||
@@ -43,6 +43,9 @@ const IDENTITY_OWNER: Record<string, string> = {
|
||||
"lib/jobs/workout-insight-generate.ts",
|
||||
"lib/workouts/list-read.ts": "lib/workouts/list-read.ts",
|
||||
"lib/workouts/sport-context.ts": "lib/workouts/sport-context.ts",
|
||||
// The doctor report's per-sport block (v1.44): sums sessions, so a session
|
||||
// two sources reported must count once, as it does on the list.
|
||||
"lib/doctor-report/workouts.ts": "lib/doctor-report/workouts.ts",
|
||||
// The only indirection in the set. The Coach block is handed rows it does not
|
||||
// read itself; the snapshot builder fetches them in parallel with every other
|
||||
// block's data. Recording the owner here rather than assuming the call site
|
||||
|
||||
Reference in new issue
Block a user