🔥 75 Hard — Sprint Report
📦 Product Review
This is a comprehensive 75 Hard challenge tracking application with advanced AI-powered journaling, fitness integration (Strava), book tracking, progress photography, and community features. The app demonstrates strong technical architecture with a robust data model, AI enrichment pipeline, and extensive feature set.
However, several features are incomplete or broken, and monetization is only partially implemented.
Product Maturity: 70% — Core features work well, but critical gaps in payments and data persistence.
Core Challenge Tracking
- Water Tracking — Target: 3785ml (1 gallon), multiple entries with timestamps, progress visualization
- Workout 1 & 2 — Manual toggle + Strava integration (auto-complete 45+ min), route maps, indoor/outdoor detection
- Diet Tracking — Meal logging (breakfast, lunch, dinner, snacks), compliance checkbox
- Reading — 10-page daily minimum, book library, voice notes + transcription, ISBN OCR scanning
- Progress Photo — Daily capture, gallery view, before/after comparison, privacy blur, timelapse generation
- Journal Entry — Text, voice, or handwriting input, photo + audio combinations, AI enrichment (mood, energy, themes, sentiment)
AI-Powered Features
- Journal Intelligence — Mood detection, energy levels, sentiment analysis, theme extraction, entity recognition, key phrases, smart summaries
- Reflection System — AI-generated daily prompts, user reflections analyzed, 7-day mood trends
- Story Arcs — Weekly/monthly narrative synthesis, theme tracking, growth suggestions
- Threads — Thematic clustering of journal entries, timeline view
Community Features (9 Event Types)
USER_JOINED,DAY_COMPLETED,FIRST_WEEK_COMPLETED,CHALLENGE_COMPLETEDLONG_WORKOUT,SIGNIFICANT_WEIGHT_CHANGE,STREAK_MILESTONEBOOK_COMPLETED,BOOK_STARTED/BOOK_HALFWAY- Reactions, confetti animations, share settings
| Integration | Status | Details |
|---|---|---|
| Supabase | ✓ Full | Auth, DB, Storage, 18 Edge Functions, RLS |
| Strava | ✓ Full | OAuth2, activity sync, route maps, token refresh |
| OpenAI (Gemini) | ✓ Working | Journal enrichment, reflections, story arcs via Lovable gateway |
| OpenLibrary | ✓ Working | Book metadata, ISBN lookup, cover images |
| Health Connect | ✗ UI Only | Components exist, no OAuth or data sync |
| Payments | ✗ None | Trial works, no Stripe/RevenueCat integration |
❌ Critical (Launch Blockers)
- No Payment Integration — App has zero revenue capability. Trial expires with no subscription flow.
- No Cloud Backup — Challenge days stored in localStorage. Users lose all progress on device change.
- No Data Export — GDPR concern. Users cannot export their data.
- Broken Trial Expiry UX — No upgrade prompt when AI trial expires. Features just stop working.
⚠️ Incomplete Features
- Video Timelapse — Service exists, no UI for generating/viewing
- Notification System — DB tables + preferences exist, no push notifications
- Story Arc Generation — Edge function exists, no manual trigger UI
- Thread Creation — Edge function exists, no UI to create/manage
🐛 Bugs
- Task ordering reset includes wrong fields
- Strava disconnect cleanup uses persistent localStorage flag
- Journal media orphans when entries deleted
🔴 P0 — Critical (Launch Blockers)
- Payment Integration — Stripe/RevenueCat (1-2 weeks)
- Challenge Day Cloud Backup — Sync localStorage to Supabase (1 week)
- Data Export — JSON/PDF/CSV export (3 days)
- Fix Expired Trial UX — Upgrade prompts (2 days)
🟡 P1 — High Priority (Next Sprint)
- Complete Health Connect Integration (1 week)
- Notification System (1 week)
- Analytics Dashboard — 75-day heatmap (1 week)
- Video Timelapse UI (3 days)
🟢 P2 — Medium Priority
- True Social Features (2 weeks)
- Story Arc & Thread UI (1 week)
- Workout History & Library (1 week)
- Enhanced Diet Tracking (1 week)
- Book Review Snippets Display (3 days)
- Donation Payment Integration (1 week)
🔵 P3 — Nice to Have
- Multi-Language Support
- Apple Health Integration
- Custom Challenges Beyond 75 Hard
- AI Voice Coach
- Gamification (badges, leaderboards)
- Desktop/Web Optimization
⚙️ Technical Audit
| Category | Technology | Version |
|---|---|---|
| Framework | React + TypeScript | 18.3 / 5.8 |
| Build | Vite + SWC | 5.4 |
| Styling | Tailwind CSS + shadcn/ui | 3.4 |
| Backend | Supabase (BaaS) | 2.81 |
| Mobile | Capacitor | 7.4 |
| State | React Query | 5.83 |
| Forms | React Hook Form + Zod | 7.61 / 3.25 |
| Charts | Recharts | 2.15 |
Application Structure
- 35 pages — Home, Books, Journal, Progress, Community, AI Insights, Settings + 8 onboarding steps
- 126 components — UI primitives (shadcn), task cards, journal, progress, reflection, story, tutorial
- 13 services — Business logic abstracting Supabase operations
- 9 custom hooks — AI status, challenge day, mobile detection, privacy, safe area
- 18 edge functions — AI enrichment, OCR, transcription, Strava, book metadata
| Table | Purpose | Key Fields |
|---|---|---|
profiles | User profiles | username, ai_status, challenge_start_date, onboarding_completed |
challenge_days | Daily tracking | date, day_number (1-75), data (JSONB tasks) |
journal_entries | Journal with AI | raw_text, mood, energy_level, themes[], sentiment, key_phrases |
journal_media | Photos/audio | type (image/audio), local_path, duration_ms |
journal_threads | Theme clusters | label, primary_theme, entry_ids[] |
journal_reflection_nuggets | AI prompts | title, body, primary_themes[], suggestion |
journal_user_reflections | User responses | user_text, themes[], sentiment, possible_insight |
trend_snapshots | AI trends | theme_counts (JSONB), average_mood, focus_suggestion |
story_arcs | AI narratives | period (7/14/30 days), title, body, key_themes[] |
books | Book library | title, author, status, isbn, metadata_* |
reading_sessions | Reading logs | from_page, to_page, notes_text, notes_audio_url |
strava_connections | OAuth tokens | access_token, refresh_token, expires_at |
strava_activities | Synced workouts | name, type, distance, moving_time, elevation |
community_events | Social feed | type (9 event types), summary_text, metadata (JSONB) |
reactions | Emoji reactions | event_id, emoji |
notifications | User notifications | type, title, body, read |
user_api_keys | BYOK keys | openai_key (plaintext ⚠️) |
✅ Strengths
- RLS enabled on all 17 tables — Proper user isolation
- JWT verification — 17/18 edge functions require auth
- OAuth2 — Standard Strava flow with token refresh
- Protected routes — Auth + onboarding checks
❌ Critical Issues
- .env committed to git — Exposes Supabase credentials
- No input validation in any edge function
- No rate limiting — AI API abuse risk
- API keys stored as plaintext — Should use Supabase Vault
- allowMixedContent: true in Capacitor config
- No GDPR consent flow
| Issue | Severity | Impact |
|---|---|---|
| .env in git | Critical | Security breach risk |
| 67% atomic design non-compliance | High | Inconsistent UI, hard maintenance |
| No test files | High | Risky refactors, no regression checks |
TypeScript as any casts | High | Runtime errors not caught |
| No error tracking (Sentry) | Medium | Blind to production errors |
| No code splitting | Medium | Large initial bundle |
| No Prettier/pre-commit hooks | Medium | Inconsistent formatting |
🎨 UX & QA Review
✅ Strengths
- Consistent HSL-based color system with proper dark mode
- Comprehensive design tokens (spacing, typography, radius, shadows)
- Atomic design methodology (atoms → molecules → organisms → templates)
- Modern gradients aligned with fitness app aesthetics
⚠️ Weaknesses
- Mixed design systems — Both custom tokens and Tailwind/shadcn coexist
- Color token duplication —
COLOURconstants (hex) vs CSS variables (HSL) - Inconsistent spacing — Some components use inline pixel values
- No documented color contrast ratios
✅ Good
- Minimum 48px touch targets enforced
- Safe area insets handled (notch, home indicator)
- Sticky header + bottom nav with backdrop blur
- Native camera integration via Capacitor
- Home-centered 5-tab bottom navigation
⚠️ Missing
- No haptic feedback on any interactions
- No swipe navigation between days
- No pull-to-refresh
- No offline mode indicator
- Images not lazy-loaded
- Limited tablet/desktop optimization
| Area | Status | Details |
|---|---|---|
| Semantic HTML | Good | Landmarks, buttons, form labels present |
| ARIA attributes | Partial | Missing aria-live, aria-expanded, aria-describedby |
| Keyboard nav | Partial | Tab order OK, no arrow keys or shortcuts |
| Color contrast | Failing | Muted text fails WCAG AA |
| Motion preferences | Missing | No prefers-reduced-motion support |
| Screen reader | Partial | Some icon buttons lack aria-label |
Critical
- Safe area inset stacking — Header 135px tall on iPhone 14 Pro (should be ~100px)
- Gradient rendering on Android — May render as solid color in some WebViews
- Bottom nav overlap — Extra 2rem creates 146px footer on Pixel 7
High Priority
- Avatar fallback shows "U" for single-char usernames
- CSS blur on large photos causes lag on low-end devices
- Strava activity dialog exceeds viewport on small screens
Medium Priority
- Long book titles break layout (no truncation)
- Dark mode toggle FOUC
- Notification badge clips on small icons
- Chip alignment inconsistency
- Add search functionality (Journal, Books, Community)
- Implement offline mode with sync queue
- Fix safe area inset bugs for notched devices
- Add comprehensive empty states with CTAs
- Respect
prefers-reduced-motionfor accessibility - Implement error boundaries for crash recovery
- Add undo/redo for task completions
- Optimize image loading with lazy load + compression
- Add keyboard navigation and shortcuts
- Implement proper loading skeletons instead of text
📋 Sprint 1 Backlog
Sprint Goal: Deliver a polished, accessible, and visually consistent UI by completing the atomic design system migration, fixing critical visual bugs, improving loading states, and enhancing mobile UX patterns.
| ID | Task | Points | Priority |
|---|---|---|---|
UI-001 | Migrate Index.tsx to Atomic Design System | 8 | P0 |
UI-002 | Fix Safe Area Inset Stacking Issues | 3 | P0 |
UI-003 | Implement Loading Skeletons Across Core Pages | 5 | P0 |
UI-004 | Add Comprehensive Empty States with CTAs | 5 | P0 |
UI-005 | Respect prefers-reduced-motion for Accessibility | 3 | P0 |
UI-006 | Fix Bottom Nav Overlap and Safe Area Bugs | 2 | P0 |
UI-007 | Add Proper Error Boundaries | 2 | P0 |
| ID | Task | Points | Priority |
|---|---|---|---|
UI-008 | Migrate Books.tsx to Atomic Design | 5 | P1 |
UI-009 | Fix Avatar Fallback Logic | 1 | P1 |
UI-010 | Add Haptic Feedback for Task Completion | 2 | P1 |
UI-011 | Optimize Image Loading with Lazy Load | 3 | P1 |
UI-012 | Add Visual Sync Status Indicators | 1 | P1 |
Current backlog: 48 points (120% of 40-point capacity)
Recommendation: Defer UI-013 (Pull-to-Refresh) and UI-014 (Progress.tsx Migration) to Sprint 2 for exact 40-point fit.
Risk Register
| Risk | Probability | Impact |
|---|---|---|
| Atomic migration breaks existing functionality | High | High |
| Safe area bugs device-specific | Medium | High |
| Team velocity unknown | High | High |
| Haptics plugin not available on web | High | Low |
- All acceptance criteria met
- Code follows atomic design system patterns
- Component is accessible (ARIA, keyboard, contrast)
- Loading states and empty states implemented
- Animations respect
prefers-reduced-motion - Works on iOS (notch) and Android (gesture nav)
- No console errors or warnings
- Tested on both light and dark modes
📖 Product Owner Stories
As a new user starting the challenge, I want helpful guidance when I haven't completed any tasks, so that I understand what to do next.
Required Empty States:
- Journal list — "No journal entries yet" + "Write First Entry" CTA
- Book list — "Your reading library is empty" + "Add Book" CTA
- Progress Photos — "No progress photos yet" + "Take Photo" CTA
- AI Insights — "Write 3+ entries to see your first insights"
- Story Arcs — "Story arcs are generated weekly from your entries"
- Community — "Your milestones will appear here"
As a user interacting with the app, I want consistent loading indicators, so that I know the app is working.
- Journal entries loading → skeleton cards
- AI enrichment processing → "Analyzing your entry..." status
- Strava sync → button disabled + "Syncing..." text
- Photo upload → progress indicator (0-100%)
- Books loading → 3-5 skeleton cards
- API failures → error message with retry option
As a user encountering errors, I want clear error messages and recovery options, so that I can resolve issues without losing data.
- Journal save failure → text preserved in local state + "Retry" button
- AI enrichment failure → warning (not blocking) + "Retry Analysis" later
- Strava token expired → "Reconnect Strava" CTA
- Photo upload error → "Photo must be JPEG/PNG and under 10MB"
- Offline → "You're offline. Changes will sync when reconnected."
- Component crash → Error boundary with "Go to Home" button
| Story | Title | Priority | Points |
|---|---|---|---|
| S1-PO-04 | Task Completion Flow Polish (animations, undo confirm) | Should | 5 |
| S1-PO-05 | Onboarding Flow Polish (validation, progress) | Should | 3 |
| S1-PO-06 | Form Validation & Feedback | Should | 3 |
| S1-PO-07 | Responsive Layout Fixes | Could | 3 |
| S1-PO-08 | Micro-interactions & Delight | Could | 2 |
🖥️ Frontend Tasks
| ID | Task | Hours | Risk | Priority |
|---|---|---|---|---|
S1-FE-01 | Fix Safe Area Inset Layout Bugs | 8 | High | HIGH |
S1-FE-02 | Optimize Photo Blur Performance | 6 | Med | MED |
S1-FE-03 | Add prefers-reduced-motion Support | 4 | Low | HIGH |
S1-FE-04 | Fix Avatar Fallback & Long Text Truncation | 2 | Low | LOW |
S1-FE-05 | Implement Consistent Loading Skeletons | 6 | Med | MED |
S1-FE-06 | Audit & Fix Color Contrast (WCAG AA) | 8 | High | HIGH |
Files: Layout.tsx, AppHeader.tsx, BottomNav.tsx, use-safe-area.ts
Problem: Header safe area stacking creates 135px padding on iPhone 14 Pro. Bottom nav adds unnecessary 2rem creating 146px footer.
// Current (broken):
pt-[calc(5.5rem+env(safe-area-inset-top))]
pb-[calc(5rem+env(safe-area-inset-bottom)+2rem)] // Extra 2rem!
// Fixed:
pt-[calc(5.5rem+env(safe-area-inset-top))]
pb-[calc(5rem+env(safe-area-inset-bottom))]
Testing: iPhone 14 Pro, Pixel 7, portrait/landscape, verify no content clipping.
WCAG 2.1 Level AA requirement. Disable all animations for users with motion sensitivity.
/* Add to tailwind.config.ts */
@media (prefers-reduced-motion: reduce) {
.animate-fade-in,
.animate-slide-up,
.animate-flash {
animation: none !important;
}
}
// confetti.ts
const shouldAnimate = !window.matchMedia(
'(prefers-reduced-motion: reduce)'
).matches;
if (shouldAnimate) confetti({ ... });
Replace all "Loading..." text with proper skeleton components matching page layouts.
// Before:
if (loading) {
return <div>Loading...</div>;
}
// After:
if (loading) {
return <DashboardSkeleton />;
}
const DashboardSkeleton = () => (
<Stack spacing={SPACING[16]}>
<Skeleton width="100%" height={120} />
<Skeleton width="100%" height={80} />
<Skeleton width="100%" height={80} />
</Stack>
);
Pages affected: Index.tsx, JournalList.tsx, Books.tsx, ProgressGallery.tsx, Community.tsx
🔧 Backend Tasks
| ID | Task | Points | Priority |
|---|---|---|---|
S1-BE-01 | Add Input Validation to Critical Edge Functions (Zod) | 5 | Critical |
S1-BE-02 | Fix Silent AI Failures with Error Propagation | 3 | High |
S1-BE-03 | Implement Database Constraint Validation | 3 | High |
S1-BE-04 | Add Bulk Delete Endpoint for Progress Photos | 2 | Medium |
Edge Function Health: 18/18 Operational
- ✅ All functions working
- ⚠️ 17/18 require JWT (only
enrich-book-metadatais public) - ❌ 0/18 have input validation
- ⚠️ 2 potential duplicates (
strava-oauthvsstrava-oauth-simple)
Add Zod schema validation to 4 critical edge functions: enrich-journal-entry, generate-reflection-nugget, strava-sync, enrich-book-metadata
// supabase/functions/_shared/validation.ts
import { z } from "zod";
export const enrichJournalEntrySchema = z.object({
entryId: z.string().uuid(),
editedText: z.string().min(1).max(10000),
date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
time: z.string().regex(/^\d{2}:\d{2}$/)
});
- Invalid UUID → 400 "Invalid entry ID format"
- Missing required fields → 400 with field name
- No database queries executed if validation fails
Replace silent fallback values with explicit error handling and retry logic.
- Standardize error types:
AIProviderError,ValidationError,NotFoundError - Add retry with exponential backoff for 429 errors
- Track enrichment status:
pending→processing→completed/failed - Remove
as anycasts injournalService.ts
-- Enforce 75-day limit
ALTER TABLE challenge_days
ADD CONSTRAINT check_day_number_range
CHECK (day_number >= 1 AND day_number <= 75);
-- Prevent duplicate reading sessions per day
CREATE UNIQUE INDEX idx_reading_sessions_unique_book_date
ON reading_sessions(user_id, book_id, date)
WHERE book_id IS NOT NULL;
-- Clear AI enrichment when journal text is edited
CREATE TRIGGER trigger_clear_enrichment_on_edit
BEFORE UPDATE ON journal_entries
FOR EACH ROW
EXECUTE FUNCTION prevent_enrichment_overwrite();
🚀 DevOps Plan
Internet
↓
Cloudflare Tunnel (75hard.tapfumamv.com)
↓ HTTPS (automatic TLS)
VPS (ports 5175/3002)
↓
systemd services → Node.js/Static Server
↓
Vite Build Output (dist/)
↓
Supabase Backend (hosted)
| Component | Technology | Port |
|---|---|---|
| Production | serve dist/ (static) | 3002 |
| Dev Server | Vite dev mode | 5175 |
| Reverse Proxy | Cloudflare Tunnel | HTTPS |
| Process Manager | systemd | — |
| ID | Task | Time |
|---|---|---|
S1-DO-01 | VPS Environment & Build Setup (npm install, build, verify) | 30 min |
S1-DO-02 | Systemd Services (75hard-prod.service, enable auto-start) | 20 min |
S1-DO-03 | Cloudflare Tunnel (config, DNS, HTTPS) | 45 min |
S1-DO-04 | Monitoring & Health Checks (cron, log retention) | 25 min |
Deployment Workflow
# Deploy
cd /root/.openclaw/workspace/75hard
npm run build
systemctl restart 75hard-prod
curl https://75hard.tapfumamv.com
# Rollback
git reset --hard <PREVIOUS_COMMIT>
npm run build
systemctl restart 75hard-prod
🎨 UI Design Tasks
| ID | Task | Priority | Effort |
|---|---|---|---|
S1-UI-01 | Fix Color Contrast Issues (WCAG AA) | HIGH | M |
S1-UI-02 | Consolidate Design System Tokens | HIGH | L |
S1-UI-03 | Fix Safe Area Inset Padding Bugs | HIGH | S |
S1-UI-04 | Implement prefers-reduced-motion | HIGH | M |
S1-UI-05 | Standardize Component Spacing & Borders | MED | M |
S1-UI-06 | Fix Typography Hierarchy | MED | S |
S1-UI-07 | Loading Skeleton Consistency | MED | M |
S1-UI-08 | Dark Mode Consistency Audit | MED | S |
| Element | Current Ratio | Required | Fix |
|---|---|---|---|
| Muted text on background | ~3.2:1 | 4.5:1 | Darken L:47% → L:42% |
| Primary links on white | ~3.8:1 | 4.5:1 | Darken L:60% → L:54% |
| Success text on light bg | ~3.5:1 | 4.5:1 | Darken L:36% → L:32% |
- Focus State System — No consistent focus indicators for keyboard navigation
- Elevation System — Shadows defined but no semantic mapping (elevation-1/2/3)
- Interactive State Tokens — No hover/active/disabled colors defined
- Grid System — No documented responsive grid (4/8/12 columns)
- Icon Size Scale — Only one size (24px), need Small (16px), Medium (20px), Large (24px), XL (32px)
✨ UX Improvements
| ID | Improvement | Priority | Effort | Impact |
|---|---|---|---|---|
S1-UX-01 | Quick Day Jump Navigation (date picker) | HIGH | 2-3 days | 70% friction reduction |
S1-UX-02 | Task Completion Undo (3s toast) | HIGH | 3-4 days | Reduces user anxiety |
S1-UX-03 | Empty State CTAs | MED | 2 days | Improves onboarding |
S1-UX-04 | Horizontal Swipe Between Days | MED | 4-5 days | Native mobile feel |
S1-UX-05 | Haptic Feedback System | MED | 1-2 days | App-native delight |
S1-UX-06 | Sync Status Indicators | MED | 2-3 days | Reduces user anxiety |
Problem: Navigating from Day 50 to Day 1 requires 49 taps on the "Previous" button.
Solution: Tap the date display → calendar overlay with challenge day numbers (1-75). Keyboard shortcut: Cmd/Ctrl + G.
Current depth: 49 taps → Target: 2 taps
Problem: Accidental task completion cannot be reversed. Users express anxiety about misclicks.
Solution: Toast-based undo pattern with 3-second window:
[✓] Workout 1 completed [UNDO]
// 3-second window, haptic on undo
// Don't sync to DB until window expires
Tiered haptic system using Capacitor Haptics plugin:
| Action | Haptic Type |
|---|---|
| Task completion | Heavy |
| Tab switch | Light |
| Day change (swipe) | Medium |
| Photo capture | Heavy |
| Day complete (7/7) | Success |
| Boundary hit | Warning |
| Action | Current Taps | Target | Status |
|---|---|---|---|
| Complete task | 1-2 | 1-2 | Good |
| Navigate to Day 1 (from 50) | 49 | 2 | Fix |
| Write journal entry | 2 | 1-2 | Good |
| Add new book | 2 | 2 | Good |
| Undo task completion | ∞ (manual) | 1 | Fix |
| View sync status | 3 | 1-2 | Improve |