Habits and streaks
LUNA tracks three habits: training, meals and steps. You don’t create or name them. Each one has a card on the home screen, its own streak, and a fixed rule for what counts as a kept day.
HabitKind values in the API: HABIT_KIND_WORKOUT,
HABIT_KIND_MEAL_ADHERENCE, HABIT_KIND_STEPS and HABIT_KIND_STEPS_ADHOC.The daily cards
| Card | How you check in | What gets journaled |
|---|---|---|
| Training | Shows today’s session from your active plan (or “Rest day” or “No plan yet”). Tap Session done. | A WORKOUT event with value 1 |
| Meals | Opens today’s plan as a checklist (“Check off what you actually ate. N% checked — 80% keeps the streak.”). | A MEAL_ADHERENCE event carrying the fraction checked so far |
| Steps | Counted by your phone. Sync now refreshes it, Change goal sets the goal, and Log an activity adds steps your phone didn’t count. | STEPS snapshots of the running total, plus STEPS_ADHOC for typed extra steps |
Checking in is meant to be a single gesture. The Training and Meals cards work against the plan you built with Luna. If there is no plan yet, the card tells you to ask for one in chat first.
What counts as a kept day
| Habit | The day qualifies when… |
|---|---|
| Training | A workout was logged (value above 0) |
| Meals | Adherence was at least 0.8, meaning 80% of the day’s plan was checked off |
| Steps | The day’s steps reached at least 80% of your daily goal |
Logging “I ate badly” every day doesn’t build a streak of keeping to a plan. Only a day that meets the rule counts.
Streak rules
- A streak breaks on a missed day, not at midnight. A run ends today if today is logged. Otherwise it ends yesterday and is marked at risk: still alive, but waiting for today. It breaks only when a whole day passes without a qualifying entry.
- Days are your days, not UTC’s. The operator sets the day boundary with
LUNA_TIMEZONE(an IANA name). If it is unset, days are counted in UTC, so an evening session in a timezone behind UTC can land on the next day. - Each habit reports
current_days,longest_days,logged_todayandat_risk(theStreakmessage inHabitService.GetScore).
Steps: counted, never typed
Steps come from the phone, never from a form field:
- Android: read from Health Connect (today’s and yesterday’s
local-day totals), which needs the
READ_STEPSpermission. - Web (debug only): read from Google Fit’s REST API. This isn’t the shipped path, and Google has closed Fit REST to new sign-ups.
The app journals the running total whenever it has risen by at least 250 steps. On the first sync of a new day, it also seals yesterday’s final total. Because these are snapshots of one total, the server takes the maximum for each day rather than adding them up.
Steps your phone never counted (a treadmill run with the phone on a shelf,
say) go through Log an activity. You type the number, and it is journaled
as STEPS_ADHOC and added to the day’s synced maximum. Luna never
estimates steps from a duration. The same dialog has a Counts as a training
day checkbox, which logs a separate workout event.
If a step read produces nothing, the app says why:
| Message | Cause |
|---|---|
| Luna has no way to count steps on this device. | Unsupported platform |
| Health Connect isn’t set up on this phone yet — install it and Luna can read the steps your phone already counts. | Health Connect missing |
| Health Connect needs an update on this phone before Luna can read your steps. | Health Connect out of date |
| Steps are not connected yet — tap Connect and Luna will start counting with you. | Not connected |
| Luna doesn’t have permission to read your steps yet. You can grant it whenever you like. | Permission refused |
| Luna could not reach your step data just now — worth another try in a moment. | Read failed |
| No steps recorded yet — your phone has not counted any today. | Genuinely no data |
The daily goal defaults to 10,000. SetStepsGoal accepts values from
1,000 to 100,000 (“a daily steps goal needs to be between 1,000 and 100,000”).
Goal changes are journaled like any other record, and the latest one wins.
The habit score
Alongside the streaks, the server computes a rolling habit score from 0.0 to 1.0. It uses the last 14 days of training, meal and step records, weighted exponentially with a 7-day half-life, so recent days count most. The score answers “am I on track”; the streak answers “don’t be the one who breaks it”.
The score drives Luna’s vitality: the creature looks leaner and quicker as it rises, and softens when it falls. The score and streaks also go into the chat prompt for the trainer and nutritionist aspects, so their coaching is grounded in what you actually logged. The prompt uses a per-day summary of steps rather than raw snapshots.
Streak days also earn the points that buy levels. See Levels, traits and skills.