Summary
The Coach treats a metric as completely absent when all of its measurements fall outside the Coach's retrieval window, even when HealthLog contains valid historical data for that metric. A vague follow-up in the same conversation can then lose the active metric and pivot to unrelated recent health data.
This was confirmed with 1,597 imported FreeStyle Libre blood-glucose readings from 3–17 April 2024 on HealthLog v1.32.39 and HealthLog-iOS 0.17.0.
Environment
- Server: HealthLog v1.32.39
- Server build:
417288d3f572
- Client: HealthLog-iOS 0.17.0 (TestFlight)
- Deployment: self-hosted
- Measurement type:
BLOOD_GLUCOSE
- Historical range: 2024-04-03 through 2024-04-17
- Reading count: 1,597
The CSV import itself is now fixed and the glucose readings are visible in HealthLog. This report is about Coach retrieval and conversation continuity, not CSV persistence.
Reproduction
- Import historical glucose data older than one year.
- Confirm that the readings exist in HealthLog and are visible on the glucose history/Insights surface.
- Open a Coach conversation and ask about the past glucose numbers.
- Observe a response such as:
I don't have any glucose data available right now.
- In the same conversation, reply with a vague follow-up such as:
Check again
- Observe that the Coach responds with unrelated recent metrics such as sleep, readiness and resting pulse instead of remaining focused on glucose.
Actual behavior
- Historical glucose is presented as if no glucose data exists at all.
- The Coach asks the user to paste values that are already stored in HealthLog.
allTime does not provide all-time retrieval.
- A same-session follow-up can abandon the active glucose topic and pivot to unrelated recent metrics.
- The response does not explain that glucose exists but lies outside the current retrieval window.
Expected behavior
When the user explicitly asks about historical glucose, the Coach should either retrieve the requested historical period or clearly state that the data exists but is outside the current retrieval scope.
A follow-up such as Check again, what about those numbers?, or how were they? should preserve the active metric from the conversation and must not pivot to unrelated domains unless the user asks for a general overview.
A suitable fallback would be:
I rechecked your glucose data specifically. HealthLog contains readings from 3–17 April 2024, but my current retrieval window cannot access that period. I should not describe this as having no glucose data.
Confirmed code path
allTime is capped at 365 days
src/lib/ai/coach/snapshot-series.ts:
case "allTime":
return 365;
The database query in src/lib/ai/coach/snapshot.ts then applies:
measuredAt: { gte: cutoff }
Measurements older than the calculated cutoff are excluded before the glucose block is built.
Historical-only glucose becomes structurally absent
src/lib/ai/coach/snapshot-blocks/glucose-block.ts filters glucose rows against glucoseCutoff. When none remain, the glucose section is omitted.
src/lib/ai/coach/tools/inventory.ts determines availability with:
The inventory therefore reports glucose as absent even though older glucose measurements exist in the database.
src/lib/ai/coach/tools/executor.ts then returns:
{"present":false,"reason":"no_data"}
This conflates:
- no glucose data exists;
- glucose exists, but none lies inside the selected window.
Tool-mode prompt encourages an unrelated pivot
src/lib/ai/coach/tools/system-addendum.ts instructs the model:
When a tool returns { present: false } or an empty result, say plainly that you have no data for that metric and pivot.
That instruction encourages the Coach to switch to unrelated recent metrics instead of preserving the active topic and explaining the retrieval limitation.
Conversation history is intended to be present
The server loads prior messages by conversationId and includes them through buildCoachTurnContext() in:
src/app/api/insights/chat/route.ts
src/lib/ai/coach/chat-request-builder.ts
Therefore this does not appear to be simple conversation persistence loss. The failure is the interaction between historical availability, ambiguous follow-up resolution and the mandatory pivot instruction.
Proposed general fix
Historical availability and retrieval
- Distinguish
no_data from no_data_in_window.
- Add all-time availability metadata per metric, including earliest date, latest date and total count.
- Support bounded historical retrieval, for example:
{
"from": "2024-04-03",
"to": "2024-04-17"
}
- Keep high-frequency reads bounded by aggregating to daily or weekly values before returning them to the model.
- Preserve the fixed trailing-30-day glucose clinical panel for current TIR/GMI/CV calculations.
- Treat historical summaries as historical context, not current clinical status.
- Either make
allTime genuinely use historical rollups or rename its present 365-day behavior to lastYear.
Conversation continuity
- Resolve short follow-ups against the most recent active metric/domain in the same conversation.
- When the active metric has
no_data_in_window, remain on that metric and explain the limitation.
- Do not pivot to unrelated domains unless the user requests a general overview or explicitly accepts alternatives.
- Consider returning a structured reason and available date bounds to the model, for example:
{
"present": false,
"reason": "no_data_in_window",
"available": {
"count": 1597,
"earliest": "2024-04-03",
"latest": "2024-04-17"
}
}
Regression tests
- Historical-only glucose older than 365 days is distinguished from truly absent glucose.
get_glucose_panel returns no_data_in_window plus availability bounds when appropriate.
- A bounded April 2024 request returns aggregated historical glucose values.
- The trailing-30-day clinical panel remains empty for old-only data and is not presented as current.
- Conversation sequence:
- user asks about old glucose;
- Coach explains historical availability/retrieval status;
- user says
Check again;
- Coach remains focused on glucose.
what about those numbers? and how were they? preserve the prior metric.
- The Coach does not cite unrelated readiness, sleep or pulse figures after a glucose-specific follow-up.
- A user with no glucose anywhere still receives the true
no_data behavior.
- Prompt/token budgets remain bounded for dense CGM histories.
Scope
This issue is separate from the resolved CSV-import defect in #640. The rows now import successfully; the remaining failure is the Coach's historical retrieval and follow-up routing behavior.
Summary
The Coach treats a metric as completely absent when all of its measurements fall outside the Coach's retrieval window, even when HealthLog contains valid historical data for that metric. A vague follow-up in the same conversation can then lose the active metric and pivot to unrelated recent health data.
This was confirmed with 1,597 imported FreeStyle Libre blood-glucose readings from 3–17 April 2024 on HealthLog v1.32.39 and HealthLog-iOS 0.17.0.
Environment
417288d3f572BLOOD_GLUCOSEThe CSV import itself is now fixed and the glucose readings are visible in HealthLog. This report is about Coach retrieval and conversation continuity, not CSV persistence.
Reproduction
Actual behavior
allTimedoes not provide all-time retrieval.Expected behavior
When the user explicitly asks about historical glucose, the Coach should either retrieve the requested historical period or clearly state that the data exists but is outside the current retrieval scope.
A follow-up such as
Check again,what about those numbers?, orhow were they?should preserve the active metric from the conversation and must not pivot to unrelated domains unless the user asks for a general overview.A suitable fallback would be:
Confirmed code path
allTimeis capped at 365 dayssrc/lib/ai/coach/snapshot-series.ts:The database query in
src/lib/ai/coach/snapshot.tsthen applies:Measurements older than the calculated cutoff are excluded before the glucose block is built.
Historical-only glucose becomes structurally absent
src/lib/ai/coach/snapshot-blocks/glucose-block.tsfilters glucose rows againstglucoseCutoff. When none remain, theglucosesection is omitted.src/lib/ai/coach/tools/inventory.tsdetermines availability with:The inventory therefore reports glucose as absent even though older glucose measurements exist in the database.
src/lib/ai/coach/tools/executor.tsthen returns:{"present":false,"reason":"no_data"}This conflates:
Tool-mode prompt encourages an unrelated pivot
src/lib/ai/coach/tools/system-addendum.tsinstructs the model:That instruction encourages the Coach to switch to unrelated recent metrics instead of preserving the active topic and explaining the retrieval limitation.
Conversation history is intended to be present
The server loads prior messages by
conversationIdand includes them throughbuildCoachTurnContext()in:src/app/api/insights/chat/route.tssrc/lib/ai/coach/chat-request-builder.tsTherefore this does not appear to be simple conversation persistence loss. The failure is the interaction between historical availability, ambiguous follow-up resolution and the mandatory pivot instruction.
Proposed general fix
Historical availability and retrieval
no_datafromno_data_in_window.{ "from": "2024-04-03", "to": "2024-04-17" }allTimegenuinely use historical rollups or rename its present 365-day behavior tolastYear.Conversation continuity
no_data_in_window, remain on that metric and explain the limitation.{ "present": false, "reason": "no_data_in_window", "available": { "count": 1597, "earliest": "2024-04-03", "latest": "2024-04-17" } }Regression tests
get_glucose_panelreturnsno_data_in_windowplus availability bounds when appropriate.Check again;what about those numbers?andhow were they?preserve the prior metric.no_databehavior.Scope
This issue is separate from the resolved CSV-import defect in #640. The rows now import successfully; the remaining failure is the Coach's historical retrieval and follow-up routing behavior.