-
Notifications
You must be signed in to change notification settings - Fork 0
feat(api): author feature-explanation markdown bundle (#84) #172
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
89dcb8b
feat(api): auto-activate streak freeze on inactive day (#108)
thomasluizon 803ea4c
refactor(api): remove manual streak-freeze activation (auto-only) (#108)
thomasluizon 7932272
fix(api): exclude soft-deleted habits from auto-freeze completion che…
thomasluizon c356f2a
feat(api): author feature-explanation markdown bundle (#84)
thomasluizon 7e9e3ac
docs(chat): freezes.md describes auto-activation model (#108)
thomasluizon 160012e
docs(chat): repoint freezes.md derived_from to auto-activation servic…
thomasluizon File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
28 changes: 28 additions & 0 deletions
28
src/Orbit.Application/Chat/Content/FeatureExplanations/ai-memory.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| --- | ||
| key: ai-memory | ||
| display_name: AI Memory | ||
| related_capabilities: [profile.ai-memory.write, user-facts.read] | ||
| related_surfaces: [ai-settings] | ||
| version: 1 | ||
| derived_from: | ||
| - src/Orbit.Application/Profile/Commands/SetAiMemoryCommand.cs Handle | ||
| - src/Orbit.Application/UserFacts/Commands/CreateUserFactCommand.cs Handle | ||
| - src/Orbit.Application/Common/AppConstants.cs MaxUserFacts | ||
| --- | ||
|
|
||
| # AI Memory | ||
|
|
||
| AI memory lets the assistant remember compact facts about you across conversations, so you don't have to repeat context every time. **AI memory is a Pro feature** — the toggle to turn it on requires Pro. | ||
|
|
||
| ## How it works | ||
|
|
||
| When memory is on, the assistant can save short facts it learns about you and recall them in later chats. You control this with a single on/off toggle. | ||
|
|
||
| ## Limits | ||
|
|
||
| - Saved facts are capped at **50** (`MaxUserFacts`). Once you reach the cap, you'll need to delete some before new ones can be added. | ||
| - **Duplicate facts are rejected** — if a fact with the same text already exists (ignoring case), it won't be saved again. | ||
|
|
||
| ## Turning it off | ||
|
|
||
| Turning memory off stops new facts from being stored. It's the switch that controls whether the assistant is allowed to remember anything new. |
26 changes: 26 additions & 0 deletions
26
src/Orbit.Application/Chat/Content/FeatureExplanations/freezes.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| --- | ||
| key: freezes | ||
| display_name: Streak Freezes | ||
| related_capabilities: [gamification.read] | ||
| related_surfaces: [gamification] | ||
| version: 1 | ||
| derived_from: | ||
| - src/Orbit.Domain/Entities/User.cs AwardStreakFreezeIfEligible | ||
| - src/Orbit.Domain/Entities/User.cs ApplyStreakFreeze | ||
| - src/Orbit.Infrastructure/Services/StreakFreezeAutoActivationService.cs ProcessUserAsync | ||
| - src/Orbit.Application/Common/AppConstants.cs MaxStreakFreezesAccumulated | ||
| --- | ||
|
|
||
| # Streak Freezes | ||
|
|
||
| A streak freeze protects your streak on a day you couldn't complete a habit. **Streak freezes are a Pro feature.** | ||
|
|
||
| ## Earning freezes | ||
|
|
||
| You earn **1 freeze for every 7 streak-days** (`StreakDaysPerFreeze` = 7). You can bank up to **3** freezes at once (`MaxStreakFreezesAccumulated` = 3); once you're at the cap, new milestones don't add more until one is spent. | ||
|
|
||
| ## How freezes are used | ||
|
|
||
| Freezes are **automatic** — there's nothing to tap. When you miss a day on your streak, a banked freeze is spent for you to bridge the gap, so the next completion continues the run instead of starting over. A freeze only **preserves** the streak across a missed day; it does not extend or increase it. | ||
|
|
||
| A freeze is spent automatically only when there's a streak worth protecting and you actually missed the day. It won't be used if your current streak is 0, if you already completed a habit that day, or if you've run out of banked freezes. At most **one** freeze is spent per day, and at most **3** are spent per calendar month (`MaxStreakFreezesPerMonth` = 3) — beyond that, a missed day breaks the streak as usual. | ||
|
thomasluizon marked this conversation as resolved.
|
||
39 changes: 39 additions & 0 deletions
39
src/Orbit.Application/Chat/Content/FeatureExplanations/frequencies.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| --- | ||
| key: frequencies | ||
| display_name: Habit Frequencies | ||
| related_capabilities: [habits.read, habits.write] | ||
| related_surfaces: [today] | ||
| version: 1 | ||
| derived_from: | ||
| - src/Orbit.Application/Habits/Services/HabitScheduleService.cs IsHabitDueOnDate | ||
| - src/Orbit.Application/Habits/Services/HabitScheduleService.cs GetWindowStart | ||
| - src/Orbit.Application/Habits/Services/HabitScheduleService.cs GetWindowEnd | ||
| - src/Orbit.Application/Habits/Services/HabitScheduleService.cs GetRemainingCompletions | ||
| --- | ||
|
|
||
| # Habit Frequencies | ||
|
|
||
| A habit's frequency decides which days it is due. Every recurring habit has a unit (Day, Week, Month, or Year), an interval quantity (how many of those units between occurrences), and an anchor date — the habit's due date — that the schedule aligns to. | ||
|
|
||
| ## The frequency units | ||
|
|
||
| - **Daily** — due every day, or every N days when the interval is greater than 1 (for example, every 2 days). The interval is counted from the anchor date. | ||
| - **Weekly** — due on the same weekday as the anchor, every N weeks (for example, every 2 weeks on Monday). | ||
| - **Monthly** — due on the same day-of-month as the anchor, every N months. | ||
| - **Yearly** — due on the same month and day as the anchor, every N years. | ||
|
|
||
| The interval quantity is the "every N" part. With a quantity of 1 the habit is due every period; with a quantity of 2 it is due every other period, and so on. A habit is never due before its anchor date, and never after its end date if one is set. | ||
|
|
||
| ## Specific weekdays | ||
|
|
||
| A habit can also restrict itself to specific weekdays. When weekdays are chosen, the habit is only due on a matching date if that date's weekday is in the list. This layers on top of the unit and interval. | ||
|
|
||
| ## One-time tasks | ||
|
|
||
| A one-time task has no recurring unit. It is due on exactly one date — its due date — and nowhere else. Once completed, it stops appearing. | ||
|
|
||
| ## Flexible habits | ||
|
|
||
| A flexible habit doesn't pin you to specific days. Instead it asks for **N completions per window**, where the window is one Day, one Week, one Month, or one Year. Weekly windows run Monday through Sunday (ISO week). Within a window you can log on any days you like until you hit the target. | ||
|
|
||
| Skips make flexible targets more forgiving: each skip in the window reduces the number of completions still required for that window. So if a weekly flexible habit wants 3 completions and you skip once, only 2 completions are needed that week. |
54 changes: 54 additions & 0 deletions
54
src/Orbit.Application/Chat/Content/FeatureExplanations/gamification.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| --- | ||
| key: gamification | ||
| display_name: XP, Levels, and Achievements | ||
| related_capabilities: [gamification.read] | ||
| related_surfaces: [gamification] | ||
| version: 1 | ||
| derived_from: | ||
| - src/Orbit.Application/Gamification/Services/GamificationService.cs ProcessHabitLogged | ||
| - src/Orbit.Application/Gamification/Services/GamificationService.cs ProcessGoalCompleted | ||
| - src/Orbit.Application/Gamification/LevelDefinitions.cs All | ||
| - src/Orbit.Application/Gamification/Services/GamificationService.cs CheckConsistencyAchievements | ||
| --- | ||
|
|
||
| # XP, Levels, and Achievements | ||
|
|
||
| Gamification rewards consistency with experience points (XP), levels, and achievements. **All of gamification — XP, levels, and achievements — is a Pro feature.** On the free plan no XP is earned and no achievements unlock. | ||
|
|
||
| ## Earning XP | ||
|
|
||
| - **Logging a habit** earns **10 + your current streak** XP. A habit logged on a 5-day streak gives 15 XP; the longer your streak, the more each completion is worth. | ||
| - **Completing a goal** earns **+100** XP. | ||
| - Unlocking an achievement also grants that achievement's own XP reward on top. | ||
|
|
||
| ## Levels | ||
|
|
||
| Your total XP places you on a level from 1 to 10. The thresholds are: | ||
|
|
||
| | Level | Title | XP required | | ||
| |---|---|---| | ||
| | 1 | Starter | 0 | | ||
| | 2 | Explorer | 100 | | ||
| | 3 | Orbiter | 300 | | ||
| | 4 | Navigator | 600 | | ||
| | 5 | Pilot | 1000 | | ||
| | 6 | Captain | 1500 | | ||
| | 7 | Commander | 2500 | | ||
| | 8 | Admiral | 4000 | | ||
| | 9 | Elite | 6000 | | ||
| | 10 | Legend | 10000 | | ||
|
|
||
| Level 10 (Legend) is the top — there is no XP-to-next once you reach it. | ||
|
|
||
| ## Achievements | ||
|
|
||
| Achievements unlock automatically as you hit milestones: | ||
|
|
||
| - **Consistency** — streaks of 7, 14, 30, 90, 100, and 365 days. | ||
| - **Volume** — 10, 50, 100, 500, and 1000 total completions. | ||
| - **Perfect runs** — Perfect Day (every scheduled habit done in a day), then Perfect Week (7 consecutive perfect days) and Perfect Month (30 consecutive perfect days). | ||
| - **Time of day** — Early Bird (complete a habit before 7am, 10 times) and Night Owl (after 10pm, 10 times). | ||
| - **Comeback** — return and log after 7+ days of inactivity. | ||
| - **Bad Habit Breaker** — resist a bad habit for 30 consecutive days. | ||
|
|
||
| There are also first-time achievements for creating your first habit and goal, and goal-completion tiers for completing 1, 5, and 10 goals. |
37 changes: 37 additions & 0 deletions
37
src/Orbit.Application/Chat/Content/FeatureExplanations/notifications.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| --- | ||
| key: notifications | ||
| display_name: Reminders and Notifications | ||
| related_capabilities: [notifications.read, notifications.write] | ||
| related_surfaces: [notifications] | ||
| version: 1 | ||
| derived_from: | ||
| - src/Orbit.Infrastructure/Services/ReminderSchedulerService.cs ProcessRelativeReminders | ||
| - src/Orbit.Infrastructure/Services/ReminderSchedulerService.cs ProcessScheduledReminders | ||
| - src/Orbit.Infrastructure/Services/ReminderSchedulerService.cs ShouldSendScheduledReminder | ||
| --- | ||
|
|
||
| # Reminders and Notifications | ||
|
|
||
| Reminders are sent by a background job that checks roughly **every minute**. There are two kinds, depending on whether a habit has a due time. | ||
|
|
||
| ## Relative reminders (habits with a due time) | ||
|
|
||
| If a habit has a specific due **time**, you can set "X minutes before" reminders. The job fires each one when the current local time reaches that many minutes before the due time. A habit can have several relative reminders (for example, 30 minutes before and 10 minutes before). | ||
|
|
||
| ## Scheduled reminders (habits without a due time) | ||
|
|
||
| If a habit has no due time, it uses **scheduled** reminders that fire at a time you pick, either: | ||
|
|
||
| - **same-day** — on the day the habit is due, or | ||
| - **day-before** — the day before it's due. | ||
|
|
||
| ## When reminders fire | ||
|
|
||
| A reminder is only sent for a habit that is: | ||
|
|
||
| - not completed and not a general habit, | ||
| - has reminders enabled, | ||
| - is actually **due** that day, and | ||
| - has **not yet been logged** that day. | ||
|
|
||
| Each distinct reminder is sent **once** — once a given reminder has fired for a habit on a given day, it won't fire again, so you won't be nudged twice for the same thing. |
40 changes: 40 additions & 0 deletions
40
src/Orbit.Application/Chat/Content/FeatureExplanations/paygate.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| --- | ||
| key: paygate | ||
| display_name: Free vs Pro | ||
| related_capabilities: [subscriptions.read] | ||
| related_surfaces: [subscriptions] | ||
| version: 1 | ||
| derived_from: | ||
| - src/Orbit.Application/Common/PayGateService.cs CanCreateHabits | ||
| - src/Orbit.Application/Common/PayGateService.cs CanSendAiMessage | ||
| - src/Orbit.Application/Common/PayGateService.cs CanUseRetrospective | ||
| - src/Orbit.Application/Common/AppConstants.cs DefaultFreeMaxHabits | ||
| --- | ||
|
|
||
| # Free vs Pro | ||
|
|
||
| Orbit has a free plan and a Pro plan. The free plan is fully usable for daily habit tracking; Pro raises the limits and unlocks the advanced features. | ||
|
|
||
| ## Limits on the free plan | ||
|
|
||
| - **Habits** are capped at **10** top-level habits. Sub-habits and soft-deleted habits don't count toward the cap. Pro removes the cap. | ||
| - **AI messages** are capped at **20** per month. Pro raises this to **500** per month. | ||
|
|
||
| Both plans can also earn a small bonus of extra AI messages from ad rewards, added on top of the plan limit. | ||
|
|
||
| ## What Pro unlocks | ||
|
|
||
| Upgrading to Pro unlocks: | ||
|
|
||
| - Goals | ||
| - Sub-habits | ||
| - The daily AI summary | ||
| - AI memory | ||
| - Calendar integration | ||
| - Premium color schemes | ||
| - Streak freezes | ||
| - Gamification: XP, levels, and achievements | ||
|
|
||
| ## The retrospective is yearly-only | ||
|
|
||
| The **retrospective** is the one feature that needs the **yearly** Pro plan specifically. A monthly Pro subscription does not include it; the yearly plan does. |
36 changes: 36 additions & 0 deletions
36
src/Orbit.Application/Chat/Content/FeatureExplanations/schedule-math.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| --- | ||
| key: schedule-math | ||
| display_name: Schedule and Overdue Math | ||
| related_capabilities: [habits.read] | ||
| related_surfaces: [today] | ||
| version: 1 | ||
| derived_from: | ||
| - src/Orbit.Application/Habits/Services/HabitScheduleService.cs IsMonthlyMatch | ||
| - src/Orbit.Application/Habits/Services/HabitScheduleService.cs IsYearlyMatch | ||
| - src/Orbit.Application/Habits/Services/HabitScheduleService.cs HasMissedPastOccurrence | ||
| - src/Orbit.Application/Common/AppConstants.cs DefaultOverdueWindowDays | ||
| --- | ||
|
|
||
| # Schedule and Overdue Math | ||
|
|
||
| A few scheduling rules surprise people because the calendar isn't uniform. Here is exactly how Orbit handles the tricky cases. | ||
|
|
||
| ## Monthly habits clamp to the last valid day | ||
|
|
||
| A monthly habit fires on the same day-of-month as its anchor (due) date. When a month is too short for that day, it clamps to the **last valid day** of that month instead of drifting. A habit anchored on the 31st fires on March 31 — never March 28 — and on the last day of shorter months. This keeps "the 31st" meaning the end of the month rather than slipping earlier permanently. | ||
|
|
||
| ## Yearly leap-day habits | ||
|
|
||
| A yearly habit anchored on **February 29** fires on **February 28** in non-leap years, then returns to February 29 when a leap year comes around again. | ||
|
|
||
| ## Intervals align off the anchor date | ||
|
|
||
| The "every N" interval (every 2 weeks, every 3 months, and so on) is measured from the anchor date, not from the current date. The anchor is the fixed reference point the whole schedule lines up against. | ||
|
|
||
| ## Overdue is DueDate-authoritative | ||
|
|
||
| A habit is **overdue when its due date has fallen before today**. The due date rests on the oldest unresolved occurrence; logging or skipping advances it past today. This single signal — due date earlier than today — is what marks a recurring habit overdue everywhere in the app. | ||
|
|
||
| Bad habits are never overdue (there's no "must do" expectation to miss), and flexible habits use their window instead of an overdue date. | ||
|
|
||
| The default overdue lookback window is **7** days (`DefaultOverdueWindowDays`): the day view surfaces unresolved occurrences from up to a week back so a missed day doesn't silently disappear. |
39 changes: 39 additions & 0 deletions
39
src/Orbit.Application/Chat/Content/FeatureExplanations/streaks.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| --- | ||
| key: streaks | ||
| display_name: Streaks | ||
| related_capabilities: [gamification.read] | ||
| related_surfaces: [gamification, today] | ||
| version: 1 | ||
| derived_from: | ||
| - src/Orbit.Infrastructure/Services/UserStreakService.cs ComputeCurrentStreak | ||
| - src/Orbit.Infrastructure/Services/UserStreakService.cs LoadStreakDataAsync | ||
| - src/Orbit.Infrastructure/Services/UserStreakService.cs CalendarFallback | ||
| - src/Orbit.Application/Common/AppConstants.cs MaxStreakLookbackDays | ||
| --- | ||
|
|
||
| # Streaks | ||
|
|
||
| Your streak counts how many consecutive **scheduled days** you stayed active. A scheduled day is a day where one of your recurring habits was due. The day counts toward the streak if either: | ||
|
|
||
| - you completed at least one eligible habit that day (a real completion, not a skip), or | ||
| - a streak freeze covered that day. | ||
|
|
||
| The streak is measured by walking backwards from today. If today has no completion yet, the count starts from yesterday so an unfinished today never breaks the run. Days where nothing was scheduled are simply skipped over — they neither extend nor break the streak. The first scheduled day you missed (no completion and no freeze) is where the streak stops. | ||
|
|
||
| ## What counts as a completion | ||
|
|
||
| A completion is any log with a value greater than zero on a habit that is not deleted and not a bad habit. Skips (a zero value) do not count. Bad habits never **add** scheduled days to your streak, but completing a regular habit on the same day still counts normally — bad habits just don't create the "must do something today" expectation. | ||
|
|
||
| ## Which habits create scheduled days | ||
|
|
||
| Expected (scheduled) days come only from your recurring habits that are not bad habits, not general habits, and not flexible habits. One-time tasks that you've already finished stop contributing expected days going forward. So a missed flexible-habit window or a skipped general habit will not break your streak. | ||
|
|
||
| ## Brand-new users | ||
|
|
||
| If you have no recurring habits at all yet, the streak falls back to simple **calendar-day adjacency**: completing a habit on back-to-back calendar days builds the streak, so you aren't penalized before you've set up any schedule. | ||
|
|
||
| ## Lookback limit | ||
|
|
||
| Streak calculation looks back at most **365** days (`MaxStreakLookbackDays`). Activity older than a year does not extend the current streak. | ||
|
|
||
| The longest streak is tracked separately by scanning your full scheduled-day history for the longest unbroken run; it never decreases when the current streak resets. |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.