mobieusKnow Handling workout sync tickets History #511
Author
Patrick Bass
Submitted
Aug 24, 2026 7:33am
Summary
Video posting: what unlocks it, what mobieusVerified opens, and what to escalate
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.
to one person, and it is labeled 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. The setting exists because members ask for
+ it; the default states our position. It is under More, then Preferences, then
+ Workouts.
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 ==
+ Do not tell a member that logging is a paid feature. It is not, and the pricing page
+ says so.
'''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.
+ '''Free:''' logging sessions, the whole exercise library, custom exercises, personal
+ record detection, full history, three saved routines, and reading and replying to a
+ coach's comments.
'''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.
+ '''Premium:''' programs and the training calendar, the volume and strength analytics,
+ progressive overload suggestions, unlimited routines, and CSV export.
'''Free, and neither of these is a feature:'''
+ A member who hits the routine cap gets a 402 with the upgrade copy. A member told
+ they must pay to save a set has hit a bug - escalate it.
* '''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.

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. The setting exists because members ask for it; the default states our position. It is under More, then Preferences, then Workouts.

== Free versus Premium ==

Do not tell a member that logging is a paid feature. It is not, and the pricing page says so.

'''Free:''' logging sessions, the whole exercise library, custom exercises, personal record detection, full history, three saved routines, and reading and replying to a coach's comments.

'''Premium:''' programs and the training calendar, the volume and strength analytics, progressive overload suggestions, unlimited routines, and CSV export.

A member who hits the routine cap gets a 402 with the upgrade copy. A member told they must pay to save a set has hit a bug - escalate it.

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. The setting exists because members ask for
it; the default states our position. It is under More, then Preferences, then
Workouts.

== Free versus Premium ==

Do not tell a member that logging is a paid feature. It is not, and the pricing page
says so.

'''Free:''' logging sessions, the whole exercise library, custom exercises, personal
record detection, full history, three saved routines, and reading and replying to a
coach's comments.

'''Premium:''' programs and the training calendar, the volume and strength analytics,
progressive overload suggestions, unlimited routines, and CSV export.

A member who hits the routine cap gets a 402 with the upgrade copy. A member told
they must pay to save a set has hit a bug - escalate it.