Lab markers and range internals
This page owns the contracts that connect the marker schema, imported laboratory metadata, contextual ranges, calculated markers, and chart status. Read it with Data pipeline and Storage schema.Schema ownership
The core schema currently contains 196 markers across 19 categories, including 22 entries in Calculated Ratios. Specialty adapters and user-defined custom markers extend that surface at runtime and are not part of the core count. The schema is split underjs/marker-schema/. schema.js is the public composition layer; category files own marker metadata, and schema-environment.js owns reference, optimal, guidance, and source metadata. Keep category placement clinically legible: insulin, for example, is diabetes.insulin under Glucose & Insulin Metabolism, not Hormones or Biochemistry.
Every built-in marker has two identifiers:
markerId: immutable namespaced catalog identity, such asgb:marker:insulin;dotKey: current storage and location key, such asdiabetes.insulin.
Effective-range data layers
Marker objects begin with schema fields and gain runtime fields asgetActiveData() builds the active dataset.
For ordinary markers, an adopted laboratory interval suppresses generic contextual fallback guidance. A manual range wins over the adopted interval. For estradiol, progesterone, LH, and FSH, a trustworthy per-draw phase range is deliberately date-specific and is the effective reference for that point instead of a flat interval.
getEffectiveRangeForDate() resolves the plotted/status range in this order:
- phase-specific reference for that date;
- context-specific optimal range when optimal display is active;
- static optimal range when optimal display is active;
- context-specific reference range;
- static effective reference, including personal overrides.
Range policies and labels
Range metadata distinguishes four semantics:reference: a conventional reference interval;guidance: population-, risk-, or assay-dependent orientation;target: a directional target, such as zero;contextual: no honest universal static interval.
marker-range-suggestions.js excludes custom markers and any marker with personal reference or optimal overrides. Its public GitHub issue payload contains catalog metadata only and must never include result values, profile attributes, laboratory names, dates, or filenames.
Laboratory range lifecycle
The import review adopts report ranges by default when valid bounds were extracted. EachimportSnapshots[] record retains its own interval data and adoption choice.
For a marker with ranges from multiple snapshots, selection uses:
- latest collection date;
- latest import time as the same-date tiebreaker.
refOverrides[dotKey] separates the current source from the stashed laboratory fallback. A manual edit preserves labRefMin, labRefMax, labRefDate, and labRefSnapshotId. Revert follows manual → adopted lab → schema default. Keep optimal overrides independent from reference overrides.
Normal chart surfaces show only the current effective interval. Historical per-file intervals remain available through import review rather than becoming date-varying chart bands.
Collection context
PDF text and image import ask the model for report-levelsampleTime and fasting. The review modal exposes both values before commit. Manual entry exposes the same fields.
The contract is intentionally strict:
sampleTimemeans specimen collection or blood-draw time, not processing, receipt, result, or report time;fastingistrue,false, or absent/unknown;- neither value is inferred from time of day, ordered tests, or result values;
- known values are stored at
entries[].contextand projected as[section:labCollectionContext]in AI context; collectionContextSourcesrecords snapshot ownership so re-review or deletion can restore the newest remaining same-date source or clear the field.
Menstrual draw context
cycle-draw-phases.js resolves draw context separately for every laboratory date. An explicit recorded phase wins. Calendar prediction is allowed only for a female profile with an active natural cycle, regularity marked regular, period history, and no hormonal contraception. Non-cycling states and uncertain predictions produce no phase range.
Built-in phase ranges currently apply to estradiol, progesterone, LH, and FSH and carry assay/source metadata. The chart layer renders compact pills only at measured points:
M,F,O, orLfor the phase;- optional
· D<n>when cycle day is known; - tooltip text that identifies recorded versus predicted context and the dated range.
Calculated marker contract
data-calculated-markers.js owns deterministic calculations. For each date, a directly reported supported calculation wins; the pipeline computes a value only when the canonical destination is empty. Normalization and alias migrations move imported historical variants into Calculated Ratios, preventing duplicate cards and preserving a laboratory’s rounding or method.
HOMA-IR is the intentional exception to category placement: lab-entry.js recalculates diabetes.homaIR from same-date glucose and canonical insulin and keeps it under Glucose & Insulin Metabolism.
NLR, PLR, and SII have age-aware guidance from age 45 plus sex-aware defaults. TyG is unrated when a sample is known to be non-fasting. FIB-4 is unrated under age 35 or with acute-illness context; its guidance threshold is 1.3 for ages 35–64 and 2.0 from age 65.
Free water deficit uses zero as a target. Positive values estimate missing free water relative to the sodium target. Negative values are the algebraic water-excess/low-sodium direction, not dehydration and not a standalone overhydration diagnosis.
Biological age
PhenoAge requires date of birth and its nine biomarkers, including hs-CRP rather than standard CRP. Bortz Age requires date of birth and its 22 inputs. The visible Biological Age marker averages the two when both exist and falls back to either available component; component charts remain hidden. The explicit Medical History flaglowMuscleMass === true suppresses creatinine-dependent calculations. Text-derived or inferred low-muscle context is kept separate and must not override a user’s explicit disabled setting.
Precision boundary
Chart-axis formatting is presentation-only. Y-axis labels use at most one decimal for magnitudes of 10 or more, two decimals from 1 to under 10, and three below 1. Stored values, detail cards, tooltips, exports, formulas, and AI context keep their underlying precision. Range/status rows must remain usable in narrow marker cards. The label pill, current value, and percentage/status text need shrinkable flex children or wrapping; long guidance labels must never push the trailing value beyond the card. Import-review unit controls likewise use a constrained schema picker or a custom-unit text/picker combination, with a viewport-positioned desktop menu and mobile-safe layout.Migration rules
profile-marker-alias-migrations.js remaps marker identity across:
entries[].markersand snapshot mappings;customMarkers;refOverrides;- date-scoped
manualValues,markerValueNotes, andmarkerLabels.
Demo-data contract
The bundled demo fixtures are product walkthroughs, not normative clinical datasets. Lab entries include coherentsampleTime and fasting metadata. The female fixture also records per-draw cycle day, broad/detail phase, and source so its late-follicular and luteal hormone examples agree with the UI labels and dated ranges. Keep scripts/upgrade-demos.mjs, demo fixtures, and tests/test-demo.js aligned when the entry schema changes.
Owner modules and focused verification
Use change-scoped tests for the touched owner. At minimum, cover direct-versus-computed precedence, same-date inputs, unit conversion, missing/context-gated inputs, import ownership/re-review/delete, range revert order, alias metadata migration, and recorded-versus-predicted phase behavior. GitHub Actions owns the exhaustive browser and combined-coverage runs.