This is how to answer a ticket about workouts not appearing, appearing twice, or disappearing. It assumes you can read the tenant database.
== "I logged a workout and it is not there" ==
Almost always a sync that has not completed rather than data that is gone. The apps log locally first and sync afterwards, deliberately: a set has to persist in under 100ms and a network round trip cannot promise that.
Ask, in this order:
# '''Is the phone online now?''' The app queues offline and drains on reconnect. A member on airplane mode has the session, it just has not left the device. # '''Did they force-quit during the session?''' The draft persists after every set change, so it should resume. If it did not, that is a real bug and worth an engineering ticket with the device and OS version. # '''Are they looking at the right day?''' The log date is the member's LOCAL day, taken from the client. Somebody who trained at 00:30 sees it under that date, not the previous one.
Check the server side with:
SELECT id, client_uuid, log_date, status, total_sets, created_at FROM workout_sessions WHERE user_id = ? ORDER BY started_at DESC LIMIT 10;
A row with status = 'in_progress' and an old started_at is a
session the member never finished. That is not a defect; the app will still show it
and they can complete or discard it.
== "The same workout is in there twice" ==
This should not happen, and if it does it is worth escalating rather than deleting one by hand.
Sessions are keyed on (user_id, client_uuid) with a UNIQUE index, so a
replayed sync updates in place. Two rows means two different UUIDs, which means
either two genuinely separate sessions or a client that minted a second UUID for
one workout.
Workouts read back from Apple Health or Health Connect are keyed on
(user_id, external_uid), also UNIQUE, so a session Loopa wrote out and
then read back in cannot land twice.
SELECT id, client_uuid, external_uid, source, started_at, ended_at, total_sets FROM workout_sessions WHERE user_id = ? AND log_date = ?;
If the two rows have different sources - one loopa and one
healthkit - the member logged in Loopa and a watch recorded the same
session independently. Deleting the platform-sourced one is the right fix, and it
is worth an engineering note so the time-window dedupe can be tightened.
== "My calories burned doubled" ==
A completed session is mirrored into the daily activity ledger so the dashboard, the reports and the challenge snapshots see it. The mirror's id is stored on the session, and a re-finalised session '''updates''' that mirror rather than adding a second one.
If a member's burn looks doubled, check whether the day carries two activity sessions for one workout:
SELECT id, activity_session_id, log_date, energy_kcal FROM workout_sessions WHERE user_id = ? AND log_date = ?;
A session with activity_session_id IS NULL that nevertheless has a
mirror on the day is the shape to escalate.
== "Why does it say my calories are estimated?" ==
Because they are, unless the health platform reported measured active energy for that session. Loopa estimates from the exercise's MET value, the member's body weight and the session's elapsed time. That is an average over a population applied to one person, and it is labelled rather than presented as a measurement.
This is also why '''eating back exercise calories is off by default'''. Both sides of the estimate are noisy and they compound, so a member's daily calorie target does not move when they log a session. The preference is carried on the training preferences record and is off unless it has been set through the API; there is no member-facing control for it in the apps or in the browser today, so a ticket asking where to turn it on has the right answer of "it is already off, and off is the default".
== Free versus Premium ==
'''Training is Premium, whole.''' Logging a session is part of what a member pays for, and the same tier applies on the phone and in the browser.
'''Premium:''' saving a session, the exercise library, custom exercises, reading your own history, personal records, routines and folders, programs and the training calendar, progressive overload suggestions, the volume and strength analytics, coach comments and replies, and CSV export.
'''Free, and neither of these is a feature:'''
* '''Deleting a session.''' A member whose subscription has lapsed still owns what they wrote and must be able to take it out. * '''Reporting a comment.''' Flagging content that was shown to you is a safety function.
So a member on [[handling-tier-and-ai-allowance-tickets|Loopa Basic]] who opens '''Workouts''' is
offered the upgrade rather than the logger. That is the gate doing its job, not a
defect. On the API it arrives as '''402''' with
{"error":"premium_required","feature":"workouts"}, and both apps turn that
into the purchase sheet.
Everything else on Loopa Basic is untouched: food, weight, water, vitals, micronutrients, manually logged activity and bringing a history in from another tracker are all free. If a member says the free tracker stopped taking a walk or a meal, that is worth escalating; being asked to subscribe to save a set is not.
This is how to answer a ticket about workouts not appearing, appearing twice, or
disappearing. It assumes you can read the tenant database.
== "I logged a workout and it is not there" ==
Almost always a sync that has not completed rather than data that is gone. The
apps log locally first and sync afterwards, deliberately: a set has to persist in
under 100ms and a network round trip cannot promise that.
Ask, in this order:
# '''Is the phone online now?''' The app queues offline and drains on reconnect.
A member on airplane mode has the session, it just has not left the device.
# '''Did they force-quit during the session?''' The draft persists after every set
change, so it should resume. If it did not, that is a real bug and worth an
engineering ticket with the device and OS version.
# '''Are they looking at the right day?''' The log date is the member's LOCAL day,
taken from the client. Somebody who trained at 00:30 sees it under that date,
not the previous one.
Check the server side with:
<syntaxhighlight lang="sql">
SELECT id, client_uuid, log_date, status, total_sets, created_at
FROM workout_sessions
WHERE user_id = ?
ORDER BY started_at DESC LIMIT 10;
</syntaxhighlight>
A row with <code>status = 'in_progress'</code> and an old <code>started_at</code> is a
session the member never finished. That is not a defect; the app will still show it
and they can complete or discard it.
== "The same workout is in there twice" ==
This should not happen, and if it does it is worth escalating rather than deleting
one by hand.
Sessions are keyed on <code>(user_id, client_uuid)</code> with a UNIQUE index, so a
replayed sync updates in place. Two rows means two different UUIDs, which means
either two genuinely separate sessions or a client that minted a second UUID for
one workout.
Workouts read back from Apple Health or Health Connect are keyed on
<code>(user_id, external_uid)</code>, also UNIQUE, so a session Loopa wrote out and
then read back in cannot land twice.
<syntaxhighlight lang="sql">
SELECT id, client_uuid, external_uid, source, started_at, ended_at, total_sets
FROM workout_sessions
WHERE user_id = ? AND log_date = ?;
</syntaxhighlight>
If the two rows have different sources - one <code>loopa</code> and one
<code>healthkit</code> - the member logged in Loopa and a watch recorded the same
session independently. Deleting the platform-sourced one is the right fix, and it
is worth an engineering note so the time-window dedupe can be tightened.
== "My calories burned doubled" ==
A completed session is mirrored into the daily activity ledger so the dashboard,
the reports and the challenge snapshots see it. The mirror's id is stored on the
session, and a re-finalised session '''updates''' that mirror rather than adding a
second one.
If a member's burn looks doubled, check whether the day carries two activity
sessions for one workout:
<syntaxhighlight lang="sql">
SELECT id, activity_session_id, log_date, energy_kcal
FROM workout_sessions WHERE user_id = ? AND log_date = ?;
</syntaxhighlight>
A session with <code>activity_session_id IS NULL</code> that nevertheless has a
mirror on the day is the shape to escalate.
== "Why does it say my calories are estimated?" ==
Because they are, unless the health platform reported measured active energy for
that session. Loopa estimates from the exercise's MET value, the member's body
weight and the session's elapsed time. That is an average over a population applied
to one person, and it is labelled rather than presented as a measurement.
This is also why '''eating back exercise calories is off by default'''. Both sides of
the estimate are noisy and they compound, so a member's daily calorie target does not
move when they log a session. The preference is carried on the training preferences
record and is off unless it has been set through the API; there is no member-facing
control for it in the apps or in the browser today, so a ticket asking where to turn it
on has the right answer of "it is already off, and off is the default".
== Free versus Premium ==
'''Training is Premium, whole.''' Logging a session is part of what a member pays for,
and the same tier applies on the phone and in the browser.
'''Premium:''' saving a session, the exercise library, custom exercises, reading your
own history, personal records, routines and folders, programs and the training calendar,
progressive overload suggestions, the volume and strength analytics, coach comments and
replies, and CSV export.
'''Free, and neither of these is a feature:'''
* '''Deleting a session.''' A member whose subscription has lapsed still owns what they
wrote and must be able to take it out.
* '''Reporting a comment.''' Flagging content that was shown to you is a safety
function.
So a member on [[handling-tier-and-ai-allowance-tickets|Loopa Basic]] who opens '''Workouts''' is
offered the upgrade rather than the logger. That is the gate doing its job, not a
defect. On the API it arrives as '''402''' with
<code>{"error":"premium_required","feature":"workouts"}</code>, and both apps turn that
into the purchase sheet.
Everything else on Loopa Basic is untouched: food, weight, water, vitals,
micronutrients, manually logged activity and bringing a history in from another tracker
are all free. If a member says the free tracker stopped taking a walk or a meal, that
is worth escalating; being asked to subscribe to save a set is not.