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.The Big Picture
{ system, messages, maxTokens, onStream? }. The caller assembles the prompt; the router just delivers it.
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.js / lab-context.js. 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: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.js 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, 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.
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.
Genome context tiers
lab-context.js wraps enabled Genome output in [section:genetics]. The three source controls map to independent buildGeneticsContext() options:
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 textdiet: type set OR any meal content OR noteexercise: frequency set OR types array not empty OR notesleepRest: duration set OR quality set OR issues not empty OR notestress: level set OR sources not empty OR noteloveLife: status set OR libido set OR concerns not empty OR noteenvironment: setting set OR water set OR air not empty OR note
Data Flow Diagram
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:
2. Focus Card (loadFocusCard)
Uses the lightweight buildFocusContext() (~200-400 tokens) instead of full buildLabContext() (~2000-8000 tokens). Health-goals-aware system prompt:
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:
context-card-health-dots.js; context-cards.js 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.
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.js. 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:
5. Persona Generator (generateCustomPersonality)
Standalone creative prompt — no lab data involved:
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:
Caching Strategy
Each AI feature has independent caching to avoid redundant API calls:Token Budget
Key Design Decisions
-
Two serializers:
buildLabContext()for full context (Chat, Health Dots) andbuildFocusContext()for slim context (Focus Card, ~200-400 tokens). The focus card only needs flagged markers and notable changes for a 40-word response. -
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. - 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.
-
Priority-tiered system prompt:
CHAT_SYSTEM_PROMPTuses a 4-tier structure (Core Rules → Priority Context → Lifestyle Context → Style) instead of a flat bullet list. Health goals and interpretive lens are at the top of Priority Context. - 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).
- 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.
-
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. -
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. -
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. -
Bounded therapy projection:
supplement-context.jsowns 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. -
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 schemeYYYY-MM (e.g., 2026-02). Each version is documented in the changelog below.
Evaluation Criteria
When evaluating context assembly changes, assess these 5 dimensions:- Completeness — Does the AI receive all relevant user data? Are any fields missing from the context?
- Primacy positioning — Are the most important sections (goals, lens, lab values) at the top where LLMs pay most attention?
- Signal-to-noise — Are empty cards omitted? Is the context lean and relevant, or padded with empty sections?
- Specificity — Do values include proper units, reference ranges, phase context, and trend direction?
- 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:- Source inspection —
test-audit.jsassertions read source code and verify structural properties (function existence, string patterns, section ordering) - DOM verification — Browser tests check that rendered output includes expected elements
- Manual console check —
window.buildLabContext()in browser console to inspect actual output - 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
fmtDatehelper
- 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
- All 7 lifestyle/medical cards check for actual content (not just object truthiness)
- Prevents sending
## Diet\nwith no content when user has an empty diet card object
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)
- Moved personality layer after lab data (was before):
SYSTEM_PROMPT + lab data + personality + search - Rationale: AI should process data first, then adopt persona style
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
askAIAboutMarker)
- Uses effective (phase-aware) reference ranges instead of static schema ranges
- Adds trend direction with percentage change when 2+ values exist
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
- JSON.parse try-catch guard in
loadContextHealthDots()— malformed AI responses degrade gracefully (gray dots on stale cards, cached good data preserved)
js/chat.js, js/constants.js, js/views.js, js/context-cards.js, js/pdf-import.js, service-worker.js (v50→v51), test-audit.js
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 agoline — 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
buildFocusContext)
- Added
last labs <date>to compact header so focus card can caveat stale data
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
hasCardContent() (v53)
- Replaced 7 hand-written card gates with generic
hasCardContent(obj)fromjs/utils.js - Returns
trueif any field has content: strings non-empty, arrays non-empty,notefield trimmed - Cards using auto-gate: diagnoses, diet, exercise, sleep, stress, loveLife, environment
- Light & Circadian keeps custom
lc || autoLatgate (external latitude injection) - Eliminates bug class: new fields added to any card are automatically included in AI context without manual gate updates
- Diet: added
restrictions,pattern,snacksto gate (user who only sets restrictions was silently dropped) - Exercise: added
intensity,dailyMovementto gate - Sleep: added
schedule,roomTemp,environment,practicesto gate - Love Life: added
relationship,satisfaction,frequency,orgasmto gate - Environment: added
climate,waterConcerns,emf,emfMitigation,homeLight,toxins,buildingto gate
js/utils.js, js/chat.js, js/constants.js, js/views.js, js/changelog.js, service-worker.js (v52→v53), test-audit.js, test-changelog.js
2026-03 — Change History
Context card change tracking (buildLabContext)
importedData.changeHistorypopulated byrecordChange()incontext-cards.jswhenever a card save detects a field value change- New
## Context Change Timelinesection (17) appended tobuildLabContext()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
js/state.js, js/profile.js, js/context-cards.js, js/cycle.js, js/chat.js, js/export.js, tests/test-change-history.js
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:dateattribute with last data date - New
[index]block listing available lab category keys - No change to content within sections — tags are additive
js/chat.js, tests/test-prelab.js