✅ TIMER APP – FULL PRODUCT & TECH SPEC (FOR EXPO REACT NATIVE) — COMPLETE UPDATED VERSION
Specifies a full offline timer app with sessions, activities, history, sharing, and a free/pro pricing tier for Expo React Native.
What this file does
Specifies a full offline timer app with sessions, activities, history, sharing, and a free/pro pricing tier for Expo React Native.
When to use it
- Building a cross-platform timer or workout app with offline support
- Designing a session-based activity timer with reusable blocks
- Implementing a free-to-pro upgrade model with feature limits
- Creating an app that exports/imports session data via JSON
Assumes this stack
✅ TIMER APP – FULL PRODUCT & TECH SPEC (FOR EXPO REACT NATIVE) — COMPLETE UPDATED VERSION
(Fully integrated — no missing fields, no TODOs, no dangling references.)
Goal
Build a cross-platform (Android + iOS) React Native app using Expo that lets users:
- Create reusable “activity” blocks (exercises, rests, transitions, study blocks, etc.).
- Assemble these into ordered “sessions” (playlists).
- Run sessions as timers with audio/vibration cues and background-safe notifications.
- Track a personal session history (for streaks, weekly stats, quick start, etc.).
- Share individual sessions via export/import so a trainer can send a workout to a client.
- Stay fully offline: no backend, no authentication, no external API.
1. Tech Stack & Libraries
Use Expo-managed workflow.
Core stack:
- React Native with Expo
- React Navigation (bottom tabs + stack navigators)
- Zustand or similarly lightweight global state library
- AsyncStorage (via
@react-native-async-storage/async-storage) for all persistence - expo-notifications for all local notification scheduling
- expo-audio for audio cues (replaced expo-av)
- expo-haptics for vibration cues
- expo-keep-awake to prevent screen sleep during running sessions (optional toggle)
- expo-document-picker for import
- expo-sharing for export
App must function fully offline.
2. Core Concepts & Data Model (Conceptual)
All data is stored locally. No backend.
2.a) BlockTemplate (reusable “activity”)
BlockTemplate (Reusable Activity)
Represents a reusable activity in the user’s Library. Rest and Transition are no longer templates and are created only inside sessions.
Each BlockTemplate has:
-
id: string -
label: string -
type: "activity" -
mode: "duration" | "reps" -
Timing fields:
-
If
duration:durationSeconds: number
-
If
reps:reps: numberperRepSeconds: number
-
-
category: string | null
- One of the built-in categories OR a custom user-created category (Pro)
-
Optional metadata:
color: stringicon: stringnotes: string
🎨 BUILT-IN ACTIVITY CATEGORIES (FREE TIER)
These categories are always available to every user:
"Exercise""Study""Work""Household""Creative""Uncategorized"
These appear as the standard selectable chips in the Activity Editor.
🌟 CUSTOM CATEGORIES (PRO FEATURE)
Free users:
-
Cannot create custom categories.
-
Can only choose from the built-in category list.
-
When importing a session containing a custom category:
- The imported block’s category is mapped to
"Uncategorized".
- The imported block’s category is mapped to
Pro users:
-
May create unlimited custom categories.
-
Custom categories are stored in Settings:
customCategories: string[] -
Custom categories appear below a divider in the category picker.
-
Can rename or delete custom categories.
- Deleting a custom category prompts to reassign affected activities.
Pro import behavior:
-
If an imported block has a category not recognized:
- Automatically add it to
customCategories.
- Automatically add it to
📚 UPDATED LIBRARY BEHAVIOR
The Library contains Activities only, not rest/transition.
Library screen shows:
- Label
- Category
- Timing summary
- Search + category filter
“Add Activity” opens Activity Editor.
📝 UPDATED ACTIVITY EDITOR (CATEGORIES INTEGRATED)
Fields:
-
Label
-
Category picker:
- Built-ins always available
- Custom categories displayed beneath a divider (Pro)
- “+ Add Category” button (Pro only)
-
Mode: duration or reps
-
Timing inputs
-
Notes (optional)
If not Pro:
- “+ Add Category” button shows lock state and cannot be tapped.
🔁 UPDATED SESSION BUILDER (REST & TRANSITION CHANGES)
When adding a block:
1. Add Activity
- Opens Library modal to select from Activity templates.
2. Add Rest
-
Opens quick form for duration only.
-
Creates BlockInstance with:
type: "rest"category: null
3. Add Transition
-
Same flow, but:
type: "transition"
Rest and transition are not saved to Library.
📤 UPDATED EXPORT/IMPORT LOGIC
Export
- Each BlockInstance exports its
categorystring unchanged.
Import (Free)
- If category is built-in → keep it.
- If category is custom → map to
"Uncategorized".
Import (Pro)
- If category is built-in → keep it.
- If category is not known → auto-add to
customCategories.
⚙️ UPDATED SETTINGS DATA MODEL
Add:
customCategories: string[] // Pro only
isProUser: boolean // monetization to be added later
Free users have customCategories = [].
Fields:
id: stringlabel: stringtype: "activity" | "rest" | "transition"mode: "duration" | "reps"
If mode = "duration":
durationSeconds: number
If mode = "reps":
reps: numberperRepSeconds: number
Optional UI metadata:
color?: stringicon?: stringnotes?: string
2.b) BlockInstance (block inside a specific session)
A snapshot (with optional overrides) of a BlockTemplate used inside a session.
Fields:
id: stringtemplateId?: string | nulllabel: stringtype: "activity" | "rest" | "transition"mode: "duration" | "reps"durationSeconds: numberreps: numberperRepSeconds: number
2.c) SessionTemplate (playlist/session)
Represents a full session the user can run.
Fields:
id: stringname: stringitems: BlockInstance[]tags?: string[]
🔥 New Field — for Quick Start scheduling
-
scheduledDaysOfWeek?: number[]- ISO weekday indices: 1=Monday … 7=Sunday
- Allows a session to appear as “today’s session” on the Home screen.
- Not enforced — user can start any session anytime.
2.d) Settings
A simple global settings object stored locally.
Fields:
preCountdownSeconds: number(0, 3, or 5; default: 3)warningSecondsBeforeEnd: number(default: 10)enableSounds: boolean(default: true)enableVibration: boolean(default: true)keepScreenAwakeDuringSession: boolean(default: true)
🔥 New Field — History Retention
-
historyRetention: "unlimited" | "3months" | "6months" | "12months"- Default:
"unlimited" - Enforced immediately when changed, and after each new history entry.
- Default:
2.e) SessionHistoryEntry
Used for streaks, weekly stats, Quick Start fallback, and recent activity.
Fields:
id: stringsessionId: string | null(null if the session was later deleted)sessionName: string(snapshot at time of completion)completedAt: stringISO timestamp in UTCtotalDurationSeconds: number
When created: At exact moment final block ends — not when user taps “Done”.
Not created if: User manually cancels the run early.
3. Storage & Persistence (AsyncStorage)
All data is local.
Store arrays/objects in AsyncStorage via a small storage service:
- BlockTemplates →
loadBlockTemplates()/saveBlockTemplates() - SessionTemplates →
loadSessionTemplates()/saveSessionTemplates() - Settings →
loadSettings()/saveSettings() - SessionHistory →
loadSessionHistory()/saveSessionHistory()
Retention Enforcement
When:
- user changes
historyRetention, OR - new history entry is added
Then:
- If
"unlimited"→ do nothing - Otherwise → compute cutoff date and remove older entries
- Clearing old history naturally resets streaks, weekly stats, Quick Start fallback options, etc.
Also add manual controls:
- “Delete all history”
- Optional: “Delete history older than 6 months”
4. Screens & Flows
4.1 Sessions Tab (List of sessions)
This is the “Sessions” tab, separate from Home.
Displays all SessionTemplates with:
- name
- total duration
- number of blocks
Actions:
- tap → Run Session (or details screen)
- edit
- duplicate
- delete
- “+ New Session”
- “Import Session”
4.2 Block Library Tab (“Activities”)
List + create + edit + delete BlockTemplates.
4.3 Session Builder Screen
Editable list of blocks with up/down arrows for reordering. Add from library or create custom. Shows total duration and block count.
Features:
- Autosave: Changes to existing sessions are automatically saved with toast notifications
- Icon Controls: Edit (pencil), Duplicate (copy), and Delete (close) icons for quick actions
- Floating Add Buttons: Add Activity, Add Rest, and Add Transition buttons float at bottom with proper scroll padding
4.4 Run Session Screen
Handles:
- pre-countdown
- per-block countdown
- audio cues, haptics
- play/pause
- prev/next block
- "skip"
- completion modal
- Full-screen mode: Bottom navigation hidden during session run
- Back button handler: Device back button stops session (same as Stop button)
- Safe area support: Proper spacing to prevent overlap with system UI
History creation
When the final block ends:
- Immediately store a SessionHistoryEntry.
- Then apply history retention.
5. Notifications & Background Behavior
Use expo-notifications.
During a run, schedule:
- next-block notifications
- “almost done” warnings
- block-end notifications
On pause or skip:
- cancel & recalc schedule
Must work in background and screen lock.
6. Audio & Haptics
- “Almost done” cue
- “Block complete” cue
- “Session complete” cue
- All gated by Settings toggles
7. Session Sharing (Export & Import)
Export
- Serialize SessionTemplate into JSON
- Save as
.bztimer(or.json) - Use Expo Sharing to export
Import
- Document picker → parse → validate → confirm → assign new id → save
8. Settings Screen
Includes toggles + history retention UI:
- Retention choices
- Delete all history
- Optional manual prune button
9. State Management
Zustand global store containing:
- blockTemplates
- sessionTemplates
- settings
- sessionHistory
- runningSession state (currentIndex, remainingSeconds, etc.)
Load all data at startup.
Save on modification.
10. Non-Functional Requirements
- Offline-first
- Android + iOS
- Simple and clear UX
- Accessible
- Performant with small datasets
11. Home Screen (Dashboard) SPEC
The Home screen is read-only + shortcuts.
Contains four cards:
- Quick Start
- Streaks
- This Week
- Recent Activity
11.1 Data Requirements
Home uses:
SessionTemplatessessionHistory- scheduledDaysOfWeek from each session
- local current date/time
11.2 Card 1 — Quick Start
Purpose
Provide a one-tap jump straight into the most relevant session for today.
Logic
- Determine today’s weekday (ISO: 1=Mon … 7=Sun)
- Gather all sessions where
scheduledDaysOfWeekincludes today
Scenario A — One or more scheduled sessions
- If exactly one scheduled session: Show one button for that session
- If multiple scheduled sessions: Show a separate button for each scheduled session (sorted alphabetically)
- Always show scheduled sessions, even if already completed today
Scenario B — No scheduled sessions
Use most recently completed session (only if its SessionTemplate still exists).
If nothing applies
Show a placeholder:
“No quick-start session available. Create and schedule a session to enable Quick Start.”
UI
-
One or more buttons: "Quick start: {SessionName}" (one button per scheduled session)
-
Subtext:
- "Today's scheduled session" (or "Today's scheduled sessions" if multiple)
- or "Last used session" (when no scheduled sessions)
Tap → Immediately:
- Switch to Sessions tab
- Navigate to RunSessionScreen
- Begin pre-countdown
11.3 Card 2 — Streaks
Definitions
-
Session day = any date with ≥1 completed session
-
Current streak = consecutive days including today with at least one session
- If no session today → streak = 0
-
Longest streak = largest consecutive chain across history
UI:
- “Current streak: X days”
- “Longest streak: Y days”
Empty state:
- “No sessions completed yet.”
11.4 Card 3 — This Week
- Define week as Monday–Sunday
- Filter history entries into current week
- Count sessions + sum minutes
UI:
- “Sessions completed: X”
- “Total time: Y min”
11.5 Card 4 — Recent Activity
Show last 3–5 entries from history:
Format:
- “Today · {SessionName} · {Minutes} min”
- “Yesterday · …”
- “Mar 5 · …”
Optional: rows non-interactive.
Empty state:
- “No recent activity yet.”
12. Pricing & Plans (Free vs Pro)
12.1 Overview
The app uses a two-tier model:
- Free Tier
- Pro Tier (unlocks all advanced functionality)
Users may upgrade via in-app purchases through the App Store / Play Store. Pro may be purchased as a monthly subscription, yearly subscription, or lifetime one-time unlock.
12.2 Free Tier — Features & Limits
The Free tier provides a fully functional timer app with reasonable limits designed for casual users.
Free Tier Includes
✔ Create sessions and run them normally ✔ Create/save activities (up to limit) ✔ Access all built-in categories ✔ Rest & Transition blocks (unlimited) ✔ Session history (last 30 days) ✔ Home dashboard (Quick Start, Streaks, Weekly Stats, Activity Feed) ✔ Import sessions shared by Pro users ✔ Background notifications ✔ All timing features (countdown, warning, sounds, vibration)
Free Tier Limits
To keep free usage generous but encourage upgrades:
Sessions Limit
Maximum: 5 saved sessions
If user tries to create a 6th session: → Show Pro upsell modal → Explain: “Free plan allows up to 5 sessions.”
Activity Library Limit
Maximum: 20 saved activities
Rest and transitions do not count toward this number.
If the user attempts to add the 21st activity, show Pro upsell.
Categories
Only built-in categories available:
Exercise
Study
Work
Household
Creative
Uncategorized
Custom categories are locked.
History Retention
Only keeps last 30 days of session history
Streaks and stats calculated only from this window
12.3 Pro Tier — Features
Pro unlocks advanced capability intended for trainers, tutors, therapists, coaches, and power users.
Pro Unlocks Unlimited Sessions
No limit on how many sessions a user may create/store.
Unlimited Activities
No limit on saved activities in the Library.
Custom Categories
Create unlimited new categories
Rename/delete categories
Used in Activity Editor and filters
Imported sessions with custom categories auto-add them into the user’s category list
Full History
Unlimited session history retention
Streaks and stats reflect full usage
Option to export history in future versions (not required now)
Export Sessions
Pro users may export/share sessions via:
JSON file
Device share sheet
AirDrop
Messaging apps
Email, etc.
Import remains free so clients/students can receive.
Priority Features (for future expansion)
Reserved for Pro tier (not required for v1, but structurally defined):
Cloud backup & sync
Analytics / performance stats
Client/Student mode
Templates gallery
Notes per block
Multi-device sync
These do not need implementation now — this section simply future-proofs Pro.
12.4 Pro Pricing
Subscription Options
$4.99 / month
$29.99 / year (approx. 17% discount vs monthly; recommended default)
Lifetime Unlock
$54.99 one-time purchase
Includes all current and future Pro features permanently
Users may upgrade from:
Monthly → Yearly
Yearly → Lifetime Store rules manage pro-rated pricing.
12.5 Upgrade Advertising (UI Requirements)
In-app Upgrade Screen
Provide a dedicated “Go Pro” screen accessible via:
Settings
When hitting limits (sessions, activities)
When attempting to create custom categories
This screen should include:
Title: “Timer Pro”
Features list (bulleted)
Comparison table (Free vs Pro)
Prices (Monthly, Yearly, Lifetime)
One button per purchase option
Subtle Labels
On the Sessions screen:
“Free plan: Up to 5 sessions.”
On the Activities screen:
“Free plan: Up to 20 activities.”
These are unobtrusive text (small, gray), to avoid surprise limits.
12.6 Import/Export Behavior Under Pricing System
Import
Remains free for all users.
Free users: custom categories imported are mapped to "Uncategorized"
Pro users: custom categories auto-added to their list
Export
Locked behind Pro.
If a free user tries to export: → Show Pro upsell modal.
12.7 Data Model Additions
Extend Settings: isProUser: boolean // updated by purchase/restore logic customCategories: string[] // only editable in Pro
No other core data models require changes for monetization.
12.8 Handling Exceeding Limits
Sessions Limit (5)
When a free user tries to create the 6th session:
Block creation
Show Pro upgrade modal
Activity Limit (20)
When a free user tries to save the 21st Activity:
Block creation
Show Pro upgrade modal
Custom Category Creation
When a free user taps “Add Category”:
Show Pro upgrade modal instead of opening creation screen
12.9 Restore Purchases
Provide a “Restore Purchases” button in Settings for:
iOS users (required by Apple)
Android users (optional but recommended)
12.10 Offline Behavior
Purchases should be cached locally via persistent storage so Pro features remain available offline once unlocked.
13. Downgrade Behavior (When Pro Expires or Subscription is Canceled)
This section defines what happens when a user who previously had Pro features (via subscription, yearly plan, or lifetime) loses access to Pro.
The downgrade model MUST:
- Never delete existing data
- Never break user flows for viewing or running sessions
- Only restrict creation or editing of items beyond Free limits
- Maintain trust and transparency
This behavior matches the standard used by major productivity apps (Notion, Todoist, TickTick).
13.1 General Principles
-
Users never lose data because of a downgrade. All sessions, activities, categories, and history remain intact.
-
Users may always RUN any session, even if it exceeds Free limits.
-
Users may VIEW all existing activities and sessions, regardless of count.
-
Only creation or editing actions beyond the Free tier limits are blocked.
-
Custom categories remain visible, but cannot be edited or used for new items unless the user upgrades again.
-
Upsell prompts appear only when users attempt actions that require Pro.
13.2 Behavior When User Has More Items Than Free Limits Allow
If the user downgrades and currently has:
- More than 5 sessions
- More than 20 activities
- One or more custom categories
- History older than 30 days
The app behaves as follows:
13.2.1 Sessions Over Limit
Free limit: 5 sessions
If user has more than 5 sessions:
- ✔ User can view all sessions
- ✔ User can run all sessions
- ✔ User can delete sessions
- ❌ User cannot create new sessions
- ❌ User cannot duplicate sessions
Attempting to create a new session triggers:
Modal:
“You’ve reached the session limit for the free plan (5). Upgrade to Pro for unlimited sessions.”
A small banner appears at top of Sessions screen:
“Free plan: You can run your existing sessions but can create up to 5.”
13.2.2 Activities Over Limit
Free limit: 20 activities
If user has more than 20 saved activities:
- ✔ User can view all activities
- ✔ User can use existing activities in sessions
- ✔ User can delete activities
- ❌ User cannot create new activities
- ❌ User cannot duplicate existing activities
Attempting to add a new Activity triggers:
Modal:
“Free plan allows up to 20 saved activities. Upgrade to Pro for unlimited activity creation.”
A small banner appears on Activity Library:
“Free plan: Up to 20 saved activities.”
13.2.3 Custom Categories After Downgrade
Free users have 0 custom categories.
When downgrading:
- ✔ Existing custom categories remain visible
- ✔ Activities keep their custom category labels
- ❌ User cannot create new custom categories
- ❌ User cannot rename or delete custom categories
- ❌ User cannot assign a custom category to new activities
In the Activity Editor:
- The custom categories appear with a lock icon
- Selecting them triggers the Pro upgrade modal
Modal:
“Custom categories are a Pro feature.”
13.2.4 History After Downgrade
Free tier retains 30 days of history.
When downgrading:
- ✔ Existing older history remains viewable until retention enforcement triggers
- ❌ New history beyond 30 days should auto-prune based on setting
Retention enforcement should occur:
- When adding a new history entry
- When user modifies history retention setting
- When app loads (optional)
13.3 Editing Restrictions After Downgrade
Sessions that use custom categories
Users may run those sessions normally.
Editing the session is allowed except:
- Editing an Activity inside the session that uses a custom category
- Adding new blocks that exceed activity/session limits
If an edit action touches a Pro feature, show the upgrade modal.
Activities that use custom categories
User may view and use them in sessions.
But:
- Editing the Activity’s name, mode, category, or timing opens Pro modal
- Deleting is still allowed
13.4 Import/Export After Downgrade
Import
Remains free. Imported custom categories map to:
"Uncategorized"for Free users- Auto-add to
customCategoriesfor Pro users (even if previously downgraded)
Export
Locked behind Pro. Attempting to export opens Pro modal.
13.5 UI Indicators for Over-Limit Settings
When user is Free and over the limits:
Sessions screen:
Small gray text:
“Free plan: Up to 5 saved sessions.”
Activities screen:
“Free plan: Up to 20 saved activities.”
Category picker:
Custom categories shown with lock icons:
Grammar Review 🔒
Client – Sarah 🔒
PT – Knee Mobility 🔒
13.6 State Model Changes
No model changes needed beyond:
isProUser: boolean
But the UI must respond to isProUser toggling at runtime.
13.7 Summary of Downgrade Rules
Users never lose data.
Free users may:
- View/run everything
- Delete anything
- Import sessions
- Interact with older custom categories (as read-only)
Free users may NOT:
- Create > 5 sessions
- Create > 20 activities
- Create/edit custom categories
- Export sessions
- Use custom categories when creating/editing activities
- Keep unlimited history
This ensures:
- No data loss
- No surprise punishments
- Very clear Pro value
- Maximum long-term goodwill
14. Empty State for Entire Home Screen
If no sessions + no history:
- Quick Start → "Create a session"
- Streaks → placeholder
- This Week → zeros
- Recent Activity → placeholder
15. Visual / UX Notes
- Vertical scrolling cards
- Rounded corners, padded cards
- Works with light/dark mode
- Make key numbers prominent
- Icon buttons for Edit, Duplicate, Delete in Session Builder
- Toast notifications for autosave feedback
- Safe area handling for all screens
- Bottom tab bar hidden during RunSession screen
16. Suggested Implementation Milestones
Milestone 1: Data + Builder Milestone 2: Run Session Milestone 3: Notifications + Sharing Milestone 4: Home Dashboard & History Milestone 5: Pro/Free Tier System
Testing:
- Set up the test environment
“Set up a test environment for this Expo React Native app using Jest and @testing-library/react-native.
Requirements:
Unit tests for pure logic and store behavior.
Component tests for key screens.
Mock all Expo + RN native bits: expo-notifications, expo-av, expo-haptics, expo-keep-awake, AsyncStorage, and react-navigation.
Use Jest fake timers where needed for timers.
Add npm scripts: test and test:watch.”
- Unit tests for pure logic helpers
“Write unit tests for the pure helper functions in types (or wherever they live), including at least:
getBlockDurationSeconds
getBlockTimingSummary
getSessionTotalDuration
Any date / streak / history helpers (current streak, longest streak, weekly stats).
Any Quick Start selection helper if one exists (or extract that logic into a pure function and then test it).
Cover edge-cases (0 reps, weird durations, empty sessions, history with gaps in dates, etc.).”
- Tests for the global store (Zustand)
“Write Jest unit tests for the Zustand store logic, without rendering components.
Things to test:
startSession(sessionId)
Loads the correct session.
Sets runningSession, currentIndex, remainingSeconds, elapsedSecondsInSession, etc.
tickTimer()
Decrements remaining time.
Moves to the next block when a block ends.
Marks the session as complete when the last block ends.
nextBlock() / previousBlock()
Correctly update currentIndex and remainingSeconds.
stopSession()
Clears runningSession and resets relevant state.
History functions:
Adding a history entry on completion.
Retention enforcement (free vs pro, 30 days vs unlimited).
Pro/free logic:
Blocking session creation when at the free limit.
Blocking activity creation when at the free limit.
Locking custom categories and export for free users.
isProUser toggling and how it changes those checks.”
- RunSessionScreen behavior tests
“Using @testing-library/react-native, write tests for RunSessionScreen that focus on logic, not styling:
When given a valid sessionId and no existing runningSession, it calls startSession and renders the correct first block.
When there is no sessionId and no runningSession, it should not render UI and should navigate back (mock navigation and assert calls).
Simulate isPreCountdown true and count down to 0 → verify that it transitions to normal running mode. Use fake timers.
While running:
tick down the timer and ensure remainingSeconds changes.
when current block ends, it moves to the next block and triggers the block-complete cue (mock cueService).
when the last block ends, it opens the completion modal.
Pressing Stop:
Shows confirmation Alert (mock Alert).
On confirming, calls stopSession, cancels notifications, and calls the correct navigation (see below).
Hardware back (mock BackHandler):
While a session is running, it should trigger the same Stop logic.
After session is stopped/completed, back should fall through normally.”
- Navigation tests for the bug you just fixed
“Write tests around navigation behavior to prevent regression of the ‘ghost RunSession screen’ bug.
Cases to cover (mock navigation):
Started from Sessions tab:
Navigate to RunSessionScreen with a sessionId and no returnTo.
On Stop or completion, navigateBack should call navigation.reset with a single route: SessionsList.
There must be no way to get back to RunSessionScreen by pressing back from SessionsList.
Started from Home Quick Start:
Navigate to RunSessionScreen with returnTo: { tab: 'Home' }.
On Stop or completion, navigateBack should:
reset stack to SessionsList,
then ask the parent navigator to navigate to Home.
Assert both calls happen in order.
These tests should directly assert the exact navigation calls we expect, so if someone changes navigateBack later, the tests fail.”
- Quick Start logic tests
“Write unit tests for the Quick Start logic. If it isn’t isolated yet, extract it into a pure function like:
getQuickStartSession(sessions, history, today) and test that:
If there are scheduled sessions today, it uses those.
If exactly one scheduled session today → use that one.
If multiple scheduled sessions today:
Exclude scheduled sessions already completed today.
If multiple remain, pick deterministically (e.g., by name or ID).
If all are completed, still return one deterministically (no empty state).
If no scheduled sessions today:
Fall back to most recently completed session whose template still exists.
If none exist → return null / undefined to show the ‘no quick start’ message.”
- History & dashboard stats tests
“Write unit tests for the history and Home/dashboard calculations, using pure functions where possible.
Test:
Current streak calculation:
No history → 0
Non-consecutive days → correct streaks
Consecutive days including today → correct current streak
Longest streak across all history.
Weekly stats (‘This Week’):
Correctly counts number of sessions and total minutes within the current week window.
Handles week with no entries.
Recent activity list:
Returns last N entries in descending date order.
Handles fewer than N entries.
Also test that history pruning for free users correctly removes entries older than 30 days and leaves newer ones.”
- Export / import tests
“Write unit tests (or integration-style tests) for session export/import:
Export:
Given a SessionTemplate, ensure the serialized JSON contains everything needed to reconstruct it.
Verify that IDs are handled correctly (either kept or replaced on import, per spec).
Import:
Valid JSON → parsed, validated, and saved as a new SessionTemplate with a new ID.
Invalid JSON (missing name, missing items, invalid mode, etc.) → import fails with an appropriate error.
Mock the document picker and sharing APIs so tests don’t depend on real files.”
- Pro vs Free behavior tests
“Write tests for Pro/free limits and locking behavior.
Free plan limits:
Attempting to create a 6th session → blocked, Pro modal triggered.
Attempting to create a 21st activity → blocked, Pro modal triggered.
Custom category creation/editing blocked on free, with modal.
Export action blocked on free, with modal.
Pro plan:
With isProUser true, none of those actions are blocked.
Downgrade behavior:
If a user has >5 sessions and downgrades:
They can still use existing sessions.
They cannot create new ones.
If they have >20 activities and downgrade:
Existing activities still usable.
Cannot create new ones.
We don’t need to implement real purchases yet; just rely on isProUser in tests.”
Audio Cues
1. Cue Events
The app supports three cue events during a session:
-
Event A – “Almost Done” Warning
- Trigger: When
warningSecondsBeforeEndis reached for the current segment. - Purpose: Inform the user that the current segment is about to end.
- Phrase: “Wrapping up.”
- Trigger: When
-
Event B – Segment Transition
- Trigger: When the current segment ends and the next segment begins.
- Purpose: Inform the user that a new segment has started.
- Phrase: “Next.”
-
Event C – Session Completion
- Trigger: When the final segment of the session completes.
- Purpose: Inform the user that the session is finished.
- Phrase: “Session complete.”
Note: Sessions only run on the Run Session screen. Navigating away (e.g., to Settings) stops the session, so cue previews in Settings never overlap with an active session.
2. Cue Packs
The app ships with three built-in cue packs. Each pack contains audio for all three events above, using the same phrases but different styles.
-
Female Spoken Pack
- Event A: female voice saying “Wrapping up.”
- Event B: female voice saying “Next.”
- Event C: female voice saying “Session complete.”
-
Male Spoken Pack
- Event A: male voice saying “Wrapping up.”
- Event B: male voice saying “Next.”
- Event C: male voice saying “Session complete.”
-
Musical Pack
- Event A: musical “wrapping up” cue.
- Event B: musical “next” cue.
- Event C: musical “session complete” cue.
All cue audio files are bundled with the app (no network required).
3. Settings: Cue Pack Selection
Setting name (internal): audioCuePack
Type: "female" | "male" | "music"
Scope: Global app setting (applies to all sessions).
Default value: music
Settings UI:
-
Section: Audio Cues
-
Control: Cue Voice / Style
-
Options:
- “Female Voice”
- “Male Voice”
- “Musical”
-
-
Selecting an option updates
audioCuePackimmediately. All future sessions use that pack for Events A, B, and C.
4. Settings: Preview Behavior
To help users understand how each cue pack sounds, the Audio Cues section includes a preview action.
Preview target phrase:
- All previews use the Event A phrase: “Wrapping up.”
- Rationale: long enough to showcase the style, shorter than the “Session complete” phrase.
Preview UI:
-
Each option (“Female Voice”, “Male Voice”, “Musical”) shows a small Play icon/button.
-
When the Play button is pressed for a given option:
-
The app plays that option’s “Wrapping up” sample.
- Female option → female “Wrapping up.”
- Male option → male “Wrapping up.”
- Musical option → musical “Wrapping up.”
-
The preview does not change the selected cue pack unless the user explicitly selects that option.
-
Constraints:
- Previews respect the existing global sound setting (e.g., if
enableSoundsis false, previews are disabled or do nothing). - Because sessions do not run while on the Settings screen, previews never overlap with an active session.
What's inside
16 sections covering data model, screens, storage, notifications, audio, sharing, pricing, downgrade rules, and implementation milestones
Change this for your project
- Replace
Dionne-Stratton/Cadence-Studio-app-react-nativewith your own repository name - Replace
.bztimerfile extension with your own app's extension if different - Replace
$4.99 / month,$29.99 / year,$54.99with your own pricing - Replace
isProUserboolean with your actual purchase/restore logic
Where it goes
Keep in docs/ or alongside the feature. Agents read it to implement against a defined contract.
Worth borrowing
- Downgrade rules that never delete user data, only restrict creation/editing
- Quick Start logic using scheduled days of week and fallback to last session
- History retention enforced on change and after each new entry
Related Documents
GPU Selection Guide for Large Language Models (LLMs)
Guides GPU selection for LLM inference, fine-tuning, and training by mapping model sizes, precision levels, and budgets to VRAM requirements.
Community AI Agent Skills Discovery Sources
Catalogs 50+ platforms, repositories, directories, and communities for discovering and sharing AI agent skills across multiple coding tools.
ReleaseKit - Technical Requirements Document
Specifies a Go library and CLI for release automation with conventional commit parsing, validation checks, and workflow orchestration.
api_llm Specification
Defines a workspace of thin HTTP API clients for major LLM providers with no abstraction layer and explicit developer control.