> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getbased.health/llms.txt
> Use this file to discover all available pages before exploring further.

# Context assembly

> How getbased assembles lab, profile, nutrition, wearable, lens, and UI context for AI prompts.

# Context Assembly

How user-provided data becomes AI prompts. This is the core intelligence layer — every AI feature is only as good as the context it receives.

<Info>
  This page documents context construction. The [system prompt catalog](/developers/system-prompts) separately inventories every developer-controlled instruction and task-prompt family.
</Info>

## The Big Picture

```text theme={null}
Chat / AI features
  └─ enabled profile context + feature-specific instructions
       └─ capability-aware execution routing
            ├─ direct: API facade → cloud/private/custom/local model endpoint
            └─ CLI: loopback Companion → selected agent
                 ├─ local execution: scoped getbased tools + supported features
                 └─ personal gateway: enabled context, supported text-only jobs
```

Feature callers keep the familiar `{ system, messages, maxTokens, onStream? }` shape through `callAssistantFeatureAI()` in `ai-feature-routing.ts`. The direct API facade remains the provider transport, but is no longer the sole entry point for all inference. Chat selects its matching backend and preserves generation ownership. See [CLI agent provider internals](/developers/cli-agent-providers).

In-app AI context is filtered by **Manage → Context → Data sources**. Those per-profile settings live in `importedData.contextSourceSettings` and are read through `context-source-registry.ts` / `lab-context.ts`. Agent Access is intentionally different: the gateway push is controlled by **Settings → Agent Access** and builds its encrypted summary with `ignoreContextToggles: true`.

## Data Sources

Everything the AI can know about the user comes from these sources:

```
┌─────────────────────────────────────────────────────────────────────┐
│                        USER DATA SOURCES                            │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  PROFILE                    CONTEXT CARDS (9)      LAB DATA         │
│  ├─ sex                     ├─ Health Goals         ├─ entries[]    │
│  ├─ DOB (→ age)             ├─ Medical History      ├─ dates[]      │
│  └─ location/latitude       ├─ Diet                 ├─ marker values│
│                             ├─ Exercise             ├─ ref ranges   │
│  PERSONA                    ├─ Sleep & Rest         ├─ optimal ranges│
│  ├─ personality ID          ├─ Light & Circadian    ├─ phase ranges │
│  ├─ built-in style prompt   ├─ Stress               ├─ custom markers│
│  └─ custom promptText       ├─ Love Life            ├─ collection context│
│                             └─ Environment           └─ flagged results│
│  CHAT                                                               │
│  ├─ user message            OTHER                                   │
│  └─ last 30 eligible msgs   ├─ Interpretive Lens                   │
│                             ├─ Context Notes                        │
│  MARKER SCHEMA              ├─ Menstrual Cycle                      │
│  ├─ MARKER_SCHEMA           ├─ Supplements                          │
│  └─ customMarkers           ├─ User Notes                           │
│                             ├─ Genetics (SNPs, if imported)         │
│                             ├─ Nutrition aggregates                 │
│                             └─ Wearable recovery summary            │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘
```

Lab context consumes the projected `getActiveData()` view, so user-selected category organization can affect section grouping and display labels. Marker resolution still follows `storageDotKey`; changing a visual category does not duplicate a value, change its history, or alter the canonical references used by prompts and scoring.

## buildLabContext() — The Central Serializer

`buildLabContext()` in `lab-context.ts` is the single function that converts profile data into a plain-text block. For in-app callers it respects Context source toggles: Insight Context Cards, Supplements & Medications, Blood marker results, specialty lab groups, Genome tiers, Light & Sun, Meals & Nutrition, and Wearable recovery context can each be omitted. Agent Access calls the same serializer with `skipGroupFilter: true` and `ignoreContextToggles: true` after its own external-sharing permission is enabled. The Focus Card uses the lighter `buildFocusContext()` instead. Meal-photo and nutrition-label extraction use their own narrow prompt contracts; they do not receive this full profile serializer.

### Output Structure

Sections are ordered by priority — the AI sees "what are you trying to solve?" first, then "what do the numbers say?", then medical/lifestyle context. This exploits primacy bias in LLMs. The example below assumes the relevant Context sources are enabled.

Each section is wrapped in `[section:name]...[/section:name]` tags for machine-parsable extraction (used by getbased-mcp, Hermes Agent, OpenClaw). Flagged results use `[critical]...[/critical]`. Lab sections include an `updated:date` attribute.

```
Lab data for current profile (sex: female, age: 34, unit system: SI, today: 2026-02-24,
  dates: Jan 15, Feb 20, Mar 10):

[section:healthGoals]                      ←── 1. GOALS (what to optimize)
## Health Goals (Things to Solve)
### Major Priority
- Optimize thyroid function
### Mild Priority
- Improve sleep quality
[/section:healthGoals]

[section:interpretiveLens]                 ←── 2. LENS (analytical framework)
## Interpretive Lens
Quantum biology, functional medicine paradigm
[/section:interpretiveLens]

[section:labCollectionContext]             ←── DRAW METADATA (explicit fields only)
## Lab Collection Context
Explicitly reported metadata by draw. Omitted fields were not reported; do not infer them from the clock time, tests ordered, or marker values.
- 2026-01-15: collection time 07:35; fasting; cycle day 13; late follicular phase (recorded)
[/section:labCollectionContext]

[index]                                    ←── category index for selective parsing
Available sections: biochemistry, hormones, lipids
[/index]

Note: status labels below use reference ranges.

[section:biochemistry updated:2026-03-10]  ←── 3. LAB VALUES (the data)
## Biochemistry
- Glucose: Jan 15: 5.2, Feb 20: 5.0, Mar 10: 4.9 mmol/L (ref: 3.9–5.6, status: normal)
- Creatinine: Jan 15: 72, Feb 20: 70 µmol/L (ref: 53–97, status: normal)
[/section:biochemistry]

[section:hormones updated:2026-02-20]
## Hormones
- Estradiol: Jan 15: 180 [late follicular, recorded, ref 77–921], Feb 20: 95 [luteal, predicted, ref 65–380] pmol/L
  ↑ phase-aware: each eligible value shows its draw phase, source, and dated range
[/section:hormones]

[critical]                                 ←── 4. FLAGS (quick-scan summary)
## Flagged Results (Latest)
- Ferritin: 12 µg/L (LOW, range: 15–200)
[/critical]

[section:userNotes]                        ←── 5. NOTES (temporal context)
## User Notes
- Jan 10: Started new thyroid medication
- Feb 15: Feeling much better energy-wise
[/section:userNotes]

[section:diagnoses]                        ←── 6. MEDICAL CONTEXT
## Medical History / Diagnoses
- Hashimoto's (major, since 2020)
### Family history (heritable/environmental risk signal)
- father: Heart Attack (MI), onset age 52 — survived, on statin since
- mother: Type 2 Diabetes, onset age 45
- maternal grandmother: Breast Cancer, onset age 61
Notes: On levothyroxine 50mcg
[/section:diagnoses]

[section:supplements]                      ←── 7. SUPPLEMENTS
## Supplements & Medications
Source-reported product and lot data below are not personal clinical laboratory results.
Safety boundary: keep ND/NQ distinct from zero. Do not call an exposure high or unsafe unless its basis converts to personal daily intake and an applicable route- and jurisdiction-specific reference is available.
- Magnesium complex [supplement, oral; active; 2026-01-01→ongoing] | personal regimen: with food; daily, 1×/day
  active ingredients: Magnesium bisglycinate 200 mg × 1/day = 200 mg/day
  other label ingredients (excipients/fillers/coatings/capsule materials): Hypromellose capsule, Rice flour
  source quality evidence included in AI context: 1 result; user confirmed the report matches their bottle lot; 1 contaminant
Source-reported contaminant overview:
- Lead — Magnesium complex: < 0.5 mcg per serving; scheduled daily mass from 1/1 compatible result(s): up to 0.5 mcg/day from upper-bound results
[/section:supplements]

[section:menstrualCycle]                   ←── 8. CYCLE (female only)
## Menstrual Cycle
Profile: 28-day cycle (5-day period), regular, moderate flow.
Recent periods: Jan 3-Jan 7 (moderate) [Cramps, Fatigue], ...
Blood draw cycle context:
- Jan 15: Day 13 (follicular phase)
- Feb 20: Day 18 (luteal phase)
Next optimal blood draw window: Mar 3-5
[/section:menstrualCycle]

[section:diet]                             ←── 9-15. LIFESTYLE CARDS
## Diet & Digestion
Type: Mediterranean. Pattern: 3 meals.
Breakfast (07:00): eggs, avocado
Lunch (12:30): salad with chicken
Dinner (19:00): salmon, vegetables
[/section:diet]

[section:nutrition]
## Meals & Nutrition
Window: Last 30 days. Coverage: 25/30 days, 68 meals.
Daily averages: 2,140 kcal; protein 128 g; carbohydrates 205 g; fat 76 g.
Meal timing: first 08:05; last 19:20; eating window 11h 15m; fasting window 12h 45m.
[/section:nutrition]

## Exercise & Movement
Frequency: 4x/week. Types: weights, yoga. Intensity: moderate.

## Sleep & Rest
Duration: 7-8h. Quality: good. Room temp: 65°F.
Environment: blackout curtains, grounding sheet.

## Light & Circadian
Morning light: 15min. UV exposure: moderate. Latitude: 40-50°N.

## Stress
Level: moderate. Sources: work, finances.

## Love Life & Sexual Health
Status: partnered. Relationship quality: good. Libido: normal.

## Environment
Water: filtered. EMF: wifi router. Home lighting: mixed.

## Context Change Timeline                 ←── 17. CHANGE HISTORY
- Mar 1, 2026: Diet & Digestion — type: omnivore → carnivore
- Feb 15, 2026: Stress — level: high → moderate

## Additional Context Notes                ←── 18. FREETEXT NOTES
Started cold plunges in January
```

### Genome context tiers

`lab-context.ts` wraps enabled Genome output in `[section:genetics]`. The three source controls map to independent `buildGeneticsContext()` options:

| Source control | Option | Output |
| - | - | - |
| **APOE & mtDNA summary** | `includeGenomeSummary` | Combined APOE haplotype, maternal lineage, mitochondrial climate-adaptation details, and available latitude-fit context |
| **Priority SNP findings** | `includePriorityFindings` | Risk, protective, and trait findings relevant to markers that have data, bounded to 12 by default |
| **Other SNP lookup inventory** | `includeSnpInventory` | Compact inventory of every imported catalog call, including reference and neutral calls |

Each priority line carries association direction, evidence grade, and relevance grade. Routine context intentionally leaves out the longer `evidence.scope` and `relevance.context` prose. When more than 12 eligible findings exist, the serializer states how many were omitted and tells the model to use a focused gene/rsID question.

`buildFullGeneticsContext()` enables the complete inventory, full evidence details, and an unbounded priority list for explicit Genome questions. **Ask AI** on one row uses the narrower `buildSnpAIInterpretationPrompt()` path: it includes only the selected call's supported claim, interpretation context, sources, and available profile context in an editable user prompt. Both paths state that the catalog is a grounded baseline rather than a limit on broader model knowledge and require the model to distinguish evidence from inference.

### Empty-Card Guards

Cards with no meaningful content are omitted entirely (not sent as empty sections). This happens after the **Insight Context Cards** source gate. Each card checks for actual data:

* `diagnoses`: conditions array not empty OR note has text
* `diet`: type set OR any meal content OR note
* `exercise`: frequency set OR types array not empty OR note
* `sleepRest`: duration set OR quality set OR issues not empty OR note
* `stress`: level set OR sources not empty OR note
* `loveLife`: status set OR libido set OR concerns not empty OR note
* `environment`: setting set OR water set OR air not empty OR note

### Data Flow Diagram

```
importedData (localStorage)
         │
         ├─── .healthGoals ────────────────────────────────┐  ← FIRST
         ├─── .interpretiveLens ───────────────────────────┤
         │                                                 │
         └─── .entries ──┐                                 │
                         ▼                                 │
                  getActiveData()                          │
                    │                                      │
                    ├─── categories[].markers[]             │
                    │     ├─ values[] (per date)            │
                    │     ├─ refMin/refMax                  │
                    │     ├─ phaseRefRanges[] (female)      │
                    │     ├─ contextRefRanges[]             │
                    │     └─ contextOptimalRanges[]         │
                    ├─── entryContextByDate                 │
                    │     ├─ sampleTime / fasting           │
                    │     └─ recorded cycle context         │
                    │                                      │
                    └─── getAllFlaggedMarkers() ────────────┤
                                                           │
         ├─── .notes ──────────────────────────────────────┤
         ├─── .diagnoses ──────────────────────────────────┤
         ├─── .supplements ── supplement-context.js ───────┤  ← MEDICAL
         ├─── .genetics ───── dna.js ──────────────────────┤
         ├─── .menstrualCycle ─── + helper functions ──────┤
         │     ├─ getCyclePhase()                          │
         │     ├─ getBloodDrawPhases()                     │
         │     ├─ getNextBestDrawDate()                    │
         │     ├─ detectPerimenopausePattern()             │
         │     └─ detectCycleIronAlerts()                  │
         ├─── .diet ───────────────────────────────────────┤
         ├─── nutritionSummary ─ nutrition-summary-context.js ───┤
         ├─── .exercise ───────────────────────────────────┤
         ├─── .sleepRest ──────────────────────────────────┤  ← LIFESTYLE
         ├─── .lightCircadian ─── + getLatitudeFromLocation()
         ├─── .stress ─────────────────────────────────────┤
         ├─── .loveLife ───────────────────────────────────┤
         ├─── .environment ────────────────────────────────┤
         ├─── .changeHistory ──────────────────────────────┤
         └─── .contextNotes ───────────────────────────────┤  ← LAST
                                                           │
                                                           ▼
                                                  buildLabContext()
                                                       │
                                                       ▼
                                                  plain text block
```

### Meals and Nutrition context

`buildNutritionSummaryContext()` supplies a compact aggregate of reviewed meal history, capped at 2,600 characters. Normal AI context uses a 7-, 30-, or 90-day nutrition window selected per profile. It includes coverage, recorded daily averages, meal timing and fasting windows, sufficiently supported trends, and Fuel Mix signals; it omits meal names, ingredient lists, notes, photos, response check-ins, and provider response diagnostics.

When the Meals & Nutrition source is enabled and reviewed history exists, the detailed nutrition block replaces the Diet card's typical-meal lines and contaminant scan so the same behavior is not represented twice. Other Diet & Digestion fields remain available.

The Nutrition History **Ask AI** action can build an editable aggregate for **30D**, **3M**, **6M**, **1Y**, or **All**. Its request receipt suppresses the automatic nutrition block for that message, preventing duplicate context. Agent Access may include the same compact aggregate only after the separate external-sharing permission is enabled.

## Prompt Composition Per Feature

### 1. Chat Panel (`sendChatMessage`)

The most complex composition — layers 4 components. Lab context is placed before personality so the AI processes data first, then adopts the persona:

```
┌──────────────────────────────────────────────────────┐
│                    SYSTEM PROMPT                      │
│                                                      │
│  ┌────────────────────────────────────────────────┐  │
│  │ CHAT_SYSTEM_PROMPT (chat-system-prompt.js)     │  │
│  │ Priority-tiered structure:                     │  │
│  │                                                │  │
│  │ ## Core Rules                                  │  │
│  │  • NOT a doctor disclaimer                     │  │
│  │  • Reference specific values/dates             │  │
│  │  • Format with markdown                        │  │
│  │                                                │  │
│  │ ## Priority Context (apply when present)       │  │
│  │  • Health goals → prioritize by severity       │  │
│  │  • Interpretive lens → frame through experts   │  │
│  │  • Medical conditions → interpret accordingly  │  │
│  │  • Supplements → correlate start/stop dates    │  │
│  │  • Menstrual cycle → phase-aware hormones      │  │
│  │  • Notes → medication changes, symptoms        │  │
│  │                                                │  │
│  │ ## Lifestyle Context (apply when present)      │  │
│  │  • Diet, exercise, sleep, light, stress,       │  │
│  │    relationships, environment                  │  │
│  │  • Cross-cutting: cortisol/HPA axis note       │  │
│  │                                                │  │
│  │ ## Style                                       │  │
│  │  • Accessible, concise, redirect off-topic     │  │
│  └────────────────────────────────────────────────┘  │
│                       +                              │
│  ┌────────────────────────────────────────────────┐  │
│  │ "Current lab data:\n"                          │  │
│  │ buildLabContext()  ← enabled serialized context│  │
│  └────────────────────────────────────────────────┘  │
│                       +                              │
│  ┌────────────────────────────────────────────────┐  │
│  │ PERSONALITY LAYER (after data)                 │  │
│  │  • Default: (nothing added)                    │  │
│  │  • Dr. House: personality.promptAddition       │  │
│  │  • Custom: "Persona: {promptText}"             │  │
│  │  • Hosted agreement controls persona usability │  │
│  │    but is not appended to the model prompt      │  │
│  └────────────────────────────────────────────────┘  │
│                       +                              │
├──────────────────────────────────────────────────────┤
│                    MESSAGES                           │
│                                                      │
│  Up to last 30 eligible history entries              │
│  (joined/error records removed; cross-persona        │
│   assistant messages are tagged with their author)   │
│                                                      │
├──────────────────────────────────────────────────────┤
│  maxTokens: 16384 │  streaming: yes                  │
│  Automatic continuation: up to 2 follow-up calls     │
└──────────────────────────────────────────────────────┘
```

### 2. Focus Card (`loadFocusCard`)

Uses the lightweight `buildFocusContext()` (\~200-400 tokens) instead of full `buildLabContext()` (\~2000-8000 tokens). Health-goals-aware system prompt:

```
┌──────────────────────────────────────────────────────┐
│  SYSTEM (if health goals exist):                     │
│  "You are a blood work analyst. Respond with ONE     │
│   sentence, max 40 words. If the patient has health  │
│   goals listed, connect your finding to their most   │
│   relevant goal. Name the single most actionable     │
│   marker finding, its direction, and why it matters. │
│   No preamble, no disclaimer."                       │
│                                                      │
│  SYSTEM (no health goals):                           │
│  "...Name the single most important marker finding,  │
│   its direction (rising/falling/high/low), and       │
│   briefly why it matters clinically..."              │
├──────────────────────────────────────────────────────┤
│  USER: buildFocusContext()                           │
│    • Profile: sex, age, today                        │
│    • Major health goals (if any)                     │
│    • Flagged markers (latest values + ranges)        │
│    • Notable changes >20% from previous              │
│    ~200-400 tokens total                             │
├──────────────────────────────────────────────────────┤
│  maxTokens: 100  │  streaming: no                     │
├──────────────────────────────────────────────────────┤
│  CACHE: fingerprint = hash(entries + sex + DOB +      │
│         filled cards + lens + notes + cycle + supps)  │
└──────────────────────────────────────────────────────┘
```

### 3. Context Card Health Dots (`loadContextHealthDots`)

Requests structured JSON for only the stale (changed) cards. JSON.parse is wrapped in try-catch — on malformed AI responses, stale cards get gray dots while cached good data is preserved:

```
┌──────────────────────────────────────────────────────┐
│  SYSTEM: "Based on this person's lab data and        │
│  profile context, assess each profile area.           │
│  Return ONLY valid JSON with these keys..."           │
│                                                      │
│  { "diet": {"summary":"...", "dot":"...",            │
│              "tip":"..."},                      ← only   │
│    "stress": {"summary":"...", "dot":"...",           │
│                "tip":"..."} }                    stale   │
│  Summary: reported card facts only, max 24 words     │
│  Dot: green/yellow/red/gray                          │
│  Tip: max 8 words, reference specific markers        │
├──────────────────────────────────────────────────────┤
│  USER: buildLabContext()                              │
├──────────────────────────────────────────────────────┤
│  maxTokens: 2048  │  streaming: no                    │
├──────────────────────────────────────────────────────┤
│  CACHE: per-card fingerprint via getCardFingerprint() │
│         only stale cards re-fetched                   │
└──────────────────────────────────────────────────────┘
```

The implementation lives in `context-card-health-dots.ts`; `context-cards.ts` keeps compatibility exports and owns the visible enable/disable controls. Normal profiles can recalculate stale cards whenever AI is available. Demo profiles follow a stricter policy:

* Bundled summaries, dots, and tips are seeded into the per-profile cache at demo import, so ordinary exploration uses no inference.
* A non-cloud Ollama/local model recalculates stale demo cards automatically.
* Cloud and other potentially paid providers require explicit consent for the active demo profile, provider, and model. Consent is held only in memory and is cleared on reload or provider/model change.
* Without live consent, an edited card is retained but its stale insight is marked as not recalculated.

The cache stores a fingerprint per card. A fingerprint includes lab entries, the card value, shared context notes and interpretive lens, sex, and date of birth; only changed cards are sent on the next live recalculation.

### Context-card field presence

`buildLabContext()` distinguishes unanswered values from explicit answers. Falsy/absent fields are omitted, but stored strings such as `none` and `normal` are meaningful evidence and must be serialized. In particular, the Diet & Digestion block includes explicit negative symptom answers and normal appetite, while Light & Circadian includes `skinType`.

This formatter is shared by in-app AI and `sync-messenger.ts`. The in-app path honors source toggles. Agent Access calls it with `ignoreContextToggles: true` because external sharing is controlled by the separate Agent Access permission; the plaintext output is then encrypted into the gateway envelope.

### 4. PDF Import (`parseLabPDFWithAI`)

Completely different context — no user profile, just schema + raw PDF. Filename is included in the user message for multi-file disambiguation:

```
┌──────────────────────────────────────────────────────┐
│  SYSTEM: "You are a lab report data extraction       │
│  assistant..."                                        │
│                                                      │
│  + JSON.stringify(buildMarkerReference())             │
│    { "biochemistry.glucose": { name, unit, ref },    │
│      "hormones.testosterone": { ... },               │
│      ... all known + custom markers }                 │
│                                                      │
│  + extraction rules (date format, value parsing,     │
│    WBC differential, custom marker suggestions)       │
│    WBC rule at position 3 (most error-prone)         │
│                                                      │
│  + expected JSON output schema                        │
├──────────────────────────────────────────────────────┤
│  USER: "Extract all biomarker results from this      │
│  lab report (file: report.pdf):\n\n" + pdfText       │
├──────────────────────────────────────────────────────┤
│  maxTokens: 8192  │  streaming: no                   │
└──────────────────────────────────────────────────────┘
```

### 5. Persona Generator (`generateCustomPersonality`)

Standalone creative prompt — no lab data involved:

```
┌──────────────────────────────────────────────────────┐
│  SYSTEM: "You are a persona designer for getbased.  │
│  Create a thorough persona covering:"                 │
│    1. Identity & Background                           │
│    2. Communication Style                             │
│    3. Medical & Health Philosophy                     │
│    4. Analytical Approach                             │
│    5. Lifestyle & Optimization Lens                   │
│    6. Character & Personality                         │
│    7. Signature Recommendations                       │
│    8. Unconventional Views                            │
│  "400-500 words; distinguish evidence, interpretation,│
│   and speculation; do not invent credentials, quotes, │
│   endorsements, biography, or precise positions."     │
├──────────────────────────────────────────────────────┤
│  USER: "Create a comprehensive persona for: {name}"  │
├──────────────────────────────────────────────────────┤
│  maxTokens: 2048  │  streaming: yes (into textarea)  │
└──────────────────────────────────────────────────────┘
```

### 6. Per-Marker AI (`askAIAboutMarker`)

Not a separate API call — builds a user message and injects it into the chat panel. Uses effective (phase-aware) reference ranges and includes trend direction when 2+ values exist:

```
askAIAboutMarker("hormones.estradiol")
         │
         ▼
  "Tell me about my Estradiol results.
   Values: Jan 15: 180 pmol/L (follicular phase, ref 77–921),
           Feb 20: 95 pmol/L (luteal phase, ref 65–380).
   Reference range: 77–921 pmol/L. Optimal range: 200–400.
   Current status: normal.
   Trend: down 47.2% from previous.
   Note: reference ranges shown are phase-specific.
   What does this mean and should I be concerned?"
         │
         ▼
  openChatPanel(prompt) → sendChatMessage() → full Chat composition
```

## Caching Strategy

Each AI feature has independent caching to avoid redundant API calls:

| Feature | Cache Key | Fingerprint Inputs | Invalidation |
| - | - | - | - |
| Focus Card | `labcharts-{profile}-focusCard` | entries + sex + DOB + diagnoses + goals + lens + notes + supplements + marker notes | Any data change in the fingerprint |
| Health Dots | `labcharts-{profile}-contextHealth` | Per-card: lab data + card data + sex + DOB | Only changed cards re-fetched |
| Chat | `labcharts-{profile}-chat-t_{id}` | N/A (conversation history) | Persists until the user deletes the conversation |
| PDF Import | N/A | N/A | No caching |
| Persona | N/A | N/A | No caching (user saves manually) |

## Token Budget

| Feature | `maxTokens` | Input context |
| - | - | - |
| Chat | 16384 | Enabled profile context plus up to 30 eligible conversation messages |
| Focus Card | 500 | Lightweight `buildFocusContext()` output |
| Context-card assessment | 2048 | Lab/profile context plus the selected card prompt |
| PDF import | 16384 | Marker schema plus document text or vision input |
| Persona generator | 2048 | Fixed creative prompt plus the requested person, archetype, or style; no lab context |

## Key Design Decisions

1. **Two serializers**: `buildLabContext()` for full context (Chat, Health Dots) and `buildFocusContext()` for slim context (Focus Card, \~200-400 tokens). The focus card only needs flagged markers and notable changes for a 40-word response.

2. **Priority-ordered sections**: `buildLabContext()` outputs sections in priority order — goals and lens first, then lab values and flags, then medical context, then lifestyle cards. This exploits LLM primacy bias so the most important context gets the most attention.

3. **Data before persona**: In Chat, lab context is placed before the personality layer in the system prompt. This ensures the AI processes the medical data first, then adopts the persona style.

4. **Sectioned system prompt**: `CHAT_SYSTEM_PROMPT` separates core safety, range interpretation, priority context, lifestyle context, the no-data state, supplements, and output style. Health goals and interpretive lens remain at the top of Priority Context.

5. **Empty-card guards**: Cards with no meaningful content are completely omitted rather than sent as empty sections. Each card has a specific content check (not just truthiness).

6. **Phase-aware values**: Estradiol, progesterone, LH, and FSH can include per-date phase labels and phase-specific reference ranges inline. Explicit recorded draw context wins. Prediction requires a female profile with an active natural regular cycle, period history, and no hormonal contraception; uncertain and non-cycling contexts are omitted.

7. **Effective ranges**: `askAIAboutMarker()` uses the dated effective range—phase, collection-context, personal/lab, or static fallback—not blindly the static schema range. It also includes trend direction with percentage change.

8. **Robust JSON parsing**: `loadContextHealthDots()` wraps the AI response JSON.parse in try-catch. On malformed responses, stale cards get gray dots while cached good data is preserved.

9. **Context source gates**: In-app Chat, Biology Score context, and source-driven warnings respect `contextSourceSettings`. Current Focus uses its own compact serializer and currently honors the lab, insight-card, supplements, and specialty-lab group gates it imports directly. Agent Access does not reuse the in-app gates; it has a separate Settings permission and pushes an encrypted full-profile summary only when enabled.

10. **Bounded therapy projection**: `supplement-context.ts` owns therapy selection and formatting. Compact context has a 6,500-character ceiling, detail mode has 12,000, and Biology Score records have 4,500. Compact mode selects current records or periods overlapping the lab range; a therapy-specific query can select bounded history. Quality-result AI toggles are respected in every projection, passing duplicate potency rows default out, and detailed methods/limits appear only in detail mode.

11. **Explicit collection context only**: `[section:labCollectionContext]` includes known sample time, fasting state, cycle day, and recorded phase by lab date. Missing fields stay omitted. The serializer does not reinterpret processing/report time as collection time or infer fasting from the clock, ordered tests, or marker values.

## Prompt Improvement Methodology

### Versioning

Context assembly changes follow the scheme `YYYY-MM` (e.g., `2026-02`). Each version is documented in the changelog below.

### Evaluation Criteria

When evaluating context assembly changes, assess these 5 dimensions:

1. **Completeness** — Does the AI receive all relevant user data? Are any fields missing from the context?
2. **Primacy positioning** — Are the most important sections (goals, lens, lab values) at the top where LLMs pay most attention?
3. **Signal-to-noise** — Are empty cards omitted? Is the context lean and relevant, or padded with empty sections?
4. **Specificity** — Do values include proper units, reference ranges, phase context, and trend direction?
5. **Token efficiency** — Is the context appropriately sized for each feature's needs? (Focus card: \~200-400 tokens, not \~8000)

### Testing Methodology

Changes are verified through 4 layers:

1. **Source inspection** — `test-audit.ts` assertions read source code and verify structural properties (function existence, string patterns, section ordering)
2. **DOM verification** — Browser tests check that rendered output includes expected elements
3. **Manual console check** — `window.buildLabContext()` in browser console to inspect actual output
4. **Cross-feature check** — Verify that Chat, Focus Card, and Health Dots all receive appropriate context

### Changelog Format

Each entry documents:

* Version identifier (YYYY-MM)
* Changes made (grouped by category)
* Files modified
* Rationale for non-obvious decisions

## Context Assembly Changelog

### 2026-02 — Priority Reordering + Enriched Context

**Enriched header**

* Added age (computed from DOB), current date (ISO), and unit system label to `buildLabContext()` header
* Consolidated 3 inline date formatters into single `fmtDate` helper

**Section reordering** (primacy bias optimization)

* Health Goals → Interpretive Lens → Lab Values → Flagged Results → User Notes → Marker Notes → Per-Value Notes → Medical History → Supplements → Menstrual Cycle → Diet → Exercise → Sleep → Light → Stress → Love Life → Environment → Context Notes
* Rationale: AI sees "what to solve" first, then data, then medical context, then lifestyle

**Empty-card guards**

* All 7 lifestyle/medical cards check for actual content (not just object truthiness)
* Prevents sending `## Diet\n` with no content when user has an empty diet card object

**System prompt restructure** (`CHAT_SYSTEM_PROMPT`)

* Restructured from flat 20-bullet list to 4 priority tiers (Core Rules → Priority Context → Lifestyle Context → Style)
* Promoted health goals + interpretive lens to top of Priority Context
* Consolidated 4 separate cortisol/HPA mentions into one cross-cutting note
* Removed duplicate creatinine/urea from exercise section (already in diet)

**Chat prompt order**

* Moved personality layer after lab data (was before): `SYSTEM_PROMPT + lab data + personality + search`
* Rationale: AI should process data first, then adopt persona style

**Focus card optimization** (`buildFocusContext`)

* New lightweight serializer: \~200-400 tokens vs \~2000-8000 from `buildLabContext()`
* Includes: profile (sex, age, today), major health goals, flagged markers, notable changes >20%
* Health-goals-aware system prompt: connects findings to goals when present

**Per-marker AI improvements** (`askAIAboutMarker`)

* Uses effective (phase-aware) reference ranges instead of static schema ranges
* Adds trend direction with percentage change when 2+ values exist

**PDF import fixes** (`parseLabPDFWithAI`)

* Moved WBC differential rule from position 6 to position 3 (most error-prone extraction rule)
* Added filename to user message for multi-file disambiguation

**Robustness**

* JSON.parse try-catch guard in `loadContextHealthDots()` — malformed AI responses degrade gracefully (gray dots on stale cards, cached good data preserved)

**Files modified**: `js/chat.ts`, `js/constants.ts`, `js/views.ts`, `js/context-cards.ts`, `js/pdf-import.ts`, `service-worker.ts` (v50→v51), `test-audit.ts`

### 2026-02b — Staleness Signals + Absent Field Awareness + Gate Broadening

**Staleness signals** (`buildLabContext`)

* **Global**: When most recent lab results are >90 days old, inserts explicit `NOTE:` line with date and approximate months since last test
* **Per-category**: After each category's markers, if that category's latest data is >90 days old, appends `⚠ Last tested ~N months ago` line — catches stale categories even when other data is recent (e.g., old fatty acids alongside fresh CBC)
* System prompt instructs AI to recommend retesting stale categories and discuss what similar/changed results would suggest

**Focus card staleness** (`buildFocusContext`)

* Added `last labs <date>` to compact header so focus card can caveat stale data

**System prompt additions** (`CHAT_SYSTEM_PROMPT`)

* Core Rules: instruction to note when data age affects analysis relevance
* Lifestyle Context: two bullets teaching AI that missing fields = user didn't provide (not assumed default), and missing sections = user hasn't filled that area

**Auto-gating with `hasCardContent()`** (v53)

* Replaced 7 hand-written card gates with generic `hasCardContent(obj)` from `js/utils.ts`
* Returns `true` if any field has content: strings non-empty, arrays non-empty, `note` field trimmed
* Cards using auto-gate: diagnoses, diet, exercise, sleep, stress, loveLife, environment
* Light & Circadian keeps custom `lc || autoLat` gate (external latitude injection)
* Eliminates bug class: new fields added to any card are automatically included in AI context without manual gate updates

**Previously broadened empty-card gates** (v52, now superseded by auto-gating)

* Diet: added `restrictions`, `pattern`, `snacks` to gate (user who only sets restrictions was silently dropped)
* Exercise: added `intensity`, `dailyMovement` to gate
* Sleep: added `schedule`, `roomTemp`, `environment`, `practices` to gate
* Love Life: added `relationship`, `satisfaction`, `frequency`, `orgasm` to gate
* Environment: added `climate`, `waterConcerns`, `emf`, `emfMitigation`, `homeLight`, `toxins`, `building` to gate

**Files modified**: `js/utils.ts`, `js/chat.ts`, `js/constants.ts`, `js/views.ts`, `js/changelog.ts`, `service-worker.ts` (v52→v53), `test-audit.ts`, `test-changelog.ts`

### 2026-03 — Change History

**Context card change tracking** (`buildLabContext`)

* `importedData.changeHistory` populated by `recordChange()` in `context-cards.ts` whenever a card save detects a field value change
* New `## Context Change Timeline` section (17) appended to `buildLabContext()` output, showing human-readable diffs between consecutive snapshots per field
* Enables temporal correlation: AI can connect a diet change on March 1 to a lab shift on March 15

**Files modified**: `js/state.ts`, `js/profile.ts`, `js/context-cards.ts`, `js/cycle.ts`, `js/chat.ts`, `js/export.ts`, `tests/test-change-history.ts`

### 2026-03b — Section Tags

**Machine-parsable section tags** (`buildLabContext`)

* Every section wrapped in `[section:name]...[/section:name]` tags for programmatic extraction (used by getbased-mcp, Hermes Agent, OpenClaw)
* Flagged results wrapped in `[critical]...[/critical]`
* Lab category sections include `updated:date` attribute with last data date
* New `[index]` block listing available lab category keys
* No change to content within sections — tags are additive

**Files modified**: `js/chat.ts`, `tests/test-prelab.ts`

## Source Files

| File | Key Functions |
| - | - |
| `js/utils.ts` | `hasCardContent()` — generic empty-card gate for context assembly |
| `js/chat-system-prompt.ts` | `CHAT_SYSTEM_PROMPT` — sectioned base chat instruction |
| `js/context-source-registry.ts` | `CONTEXT_SOURCE_IDS`, profile-scoped source toggles, lab-group slug helpers |
| `js/context-card-dashboard-ai.ts` | Manage → Context modal, Answer grounding cards, Data sources UI |
| `js/context-card-health-dots.ts` | Card summaries/dots/tips, per-card fingerprints and cache, demo live-AI policy |
| `js/context-cards.ts` | Card rendering facade, save/refresh lifecycle, demo AI confirmation controls |
| `js/lab-context.ts` | `buildLabContext()` — central serializer (full context) |
| `js/lab-context-collection.ts` | Date-scoped collection-time, fasting, cycle-day, and recorded-phase context section |
| `js/lab-context-change-timeline.ts` | Context-card and profile-context history section |
| `js/lab-context-wearables.ts` | Wearable summary sections and structured sleep-context mismatch warnings |
| `js/supplement-context.ts` | Period-aware compact/detail therapy context, quality-evidence boundaries, and hard character limits |
| `js/supplement-medication-domain.ts` | Status, period overlap, schedule exposure, stable IDs, and compatibility migration |
| `js/supplement-quality.ts` | Per-result AI inclusion and conservative source-quality aggregation |
| `js/supplement-warnings.ts` | Claim-level mitochondrial primary-study context |
| `js/sync-messenger.ts` | Agent Access context push, encrypted gateway envelope, `ignoreContextToggles` external-sharing path |
| `js/chat-send.ts` | `sendChatMessage()` — chat prompt composition |
| `js/chat-marker-prompts.ts` | `askAIAboutMarker()` — per-marker prompt (effective ranges + trend) |
| `js/marker-context-ranges.ts` | Age-, sex-, and collection-time-aware range resolvers |
| `js/chat-personalities.ts` | `generateCustomPersonality()` — persona generator prompt |
| `js/views.ts` | `buildFocusContext()` — lightweight focus card context (\~200-400 tokens) |
| `js/views.ts` | `loadFocusCard()` — focus card prompt (health-goals-aware) |
| `js/context-cards.ts` | `recordChange(field)` — compatibility facade for timestamped context snapshots |
| `js/pdf-import.ts` | `parseLabPDFWithAI()` — PDF import prompt (filename included) |
| `js/pdf-import.ts` | `buildMarkerReference()` — marker schema serializer |
| `js/api.ts` | `callClaudeAPI()` — provider router |
| `js/data.ts` | `getActiveData()` — data pipeline (feeds buildLabContext) |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.