Built with Node 22 / Vite. Includes DW brand theme, ChatCoach,
category-grouped integrations, AgentConnect with all providers,
Dashboard checklist, Settings help section, gear icon.
AgentConnect.jsx rewritten:
- Option 1: Built-in AI Coach — lists all 8 providers with free tier
badges and 'Get key' links, step-by-step setup instructions
- Option 2: External AI Agent (MCP) — Claude Desktop (with Windows
path), Claude Code CLI, Cursor, Windsurf/other
- Page renamed from 'Connect Your AI Agent' to 'Connect AI'
Settings.jsx:
- Added Help & Support section: Report a Bug (GitHub Issues),
Ask a Question (GitHub Discussions), Star on GitHub
React hooks must be called unconditionally on every render. The
checklist useState was at line 89, after 'if (loading) return' at
line 70 — hooks violation crashed the entire React app (blank screen).
Moved to line 38 with the other state declarations.
Timezone:
- Add diligence/utils/dates.py with today_for_user(), now_for_user(), day_start_utc()
- Replace all 20 date.today() calls across 10 files with timezone-aware helpers
- Remove hardcoded Asia/Bangkok offset hack in nutrition router
- Change defaults from Asia/Bangkok to UTC in user model, nutrition model, config
- Add timezone field to RegisterRequest schema
- Set user.timezone from browser detection during registration
- Frontend sends Intl.DateTimeFormat().resolvedOptions().timeZone on register
Branding:
- Login page: Fitness Rewards -> Diligence, updated tagline and emoji
- index.html title: Fitness Rewards -> Diligence
Frontend rebuild:
- Fresh Vite build with all changes (index-CsKoBN0D.js)
- Nav bar, timezone detection, and branding all verified in built bundle
- New AgentConnect.jsx page with MCP URL, token, setup instructions
- Claude Desktop config snippet with copy button
- Claude Code CLI command with copy button
- Agent capabilities overview
- Added to bottom nav bar (6th item, plug icon)
- Backend /api/agent/config endpoint returns deployment-aware MCP details
- Nav padding adjusted for 6 items
- Frontend rebuilt with new page
Bug: API_TOKEN replacement ran before .env existed, and before the
existence check. On a clean clone the script errored immediately.
Fixed flow: banner → existence check → generate SECRET_KEY →
generate API_TOKEN → copy .env.example → replace both keys.
Also adds MCP connection info to output (matching setup.sh).
Root cause: ai_provider.py assumed a single JSON blob in an
'encrypted_value' column, but IntegrationConfig stores each credential
as a separate row with config_key + config_value columns.
Fixes:
- Query groups rows by provider, decrypts each config_value separately
- Correct decrypt_value(secret_key, ciphertext) argument order
- Filter by user_id so providers are per-user
- Callers (ai_chat.py, chat()) now pass user_id through
SEC-11 removed algorithm from Settings but missed 3 call sites:
- auth.py line 68: jwt.decode in get_current_user
- integrations.py lines 58, 120: jwt.decode in OAuth callbacks
All now use ALGORITHM constant or inline 'HS256'.
The SEC-06 replacement swallowed the trailing comma into the comment:
allow_credentials=False # comment, ← comma in comment
Fixed to:
allow_credentials=False, # comment ← comma after value
Previous commit wiped App.jsx due to emoji encoding error in the
patch script. Restored from Stream A commit (c075eb9), then applied
ChatCoach changes using a file-based Python script to handle unicode.
- import ChatCoach from './pages/ChatCoach' (line 21)
- NavLink /chat with brain emoji + 'Coach' label (line 95)
- Route /chat -> ProtectedRoute -> ChatCoach (line 125)
WHOOP developer registration requires device ownership. Sync service
will be built against their documented API; first WHOOP-owning user
serves as the integration tester. Focus on Garmin and Oura first.
Frontend:
- ChatCoach.jsx: streaming chat UI with SSE, provider indicator, suggestion
chips, 'no AI configured' state with setup link
- App.jsx: import + /chat route + nav bar (More → Coach with brain emoji)
- api.js: getAIStatus() method
Documentation:
- README.md: 'Built-in AI Coach' section with 8-provider table, external
agent config snippets for Claude Desktop, Claude Code, Cursor, Windsurf,
and COROS multi-MCP pattern
- AGENT_GUIDE.md: multi-device integration, Strava AI/ML warning,
provider-agnostic statement
Nav bar: Home | Log | Keto | Programs | Coach
Settings still reachable at /settings (linked from Coach setup prompt)
Abstract base class for device sync services (device_sync_base.py):
- Standard OAuth token management with automatic refresh
- Credential decryption from integration_configs table
- Activity import pipeline with deduplication by external_id
- Webhook validation and handling interface
- get_valid_token(), get_credentials(), import_activity() helpers
Garmin, WHOOP, and Oura sync service implementations follow when
API credentials are obtained (Scot action items):
- Apply for Garmin Developer Program (DiligenceWorks Pte. Ltd.)
- Register WHOOP developer app at developer.whoop.com
- Register Oura app at cloud.ouraring.com
AI coaching service (backend/app/services/ai_provider.py):
- Two code paths: OpenAI-compatible (6 providers) + Anthropic (Claude)
- Streaming SSE responses via httpx async
- System prompt built from AGENT_GUIDE + live user context
- Graceful error handling for all provider failures
- Gemini adapter for Google's generateContent API
Chat endpoint (backend/app/routers/ai_chat.py):
- POST /api/ai/chat — SSE streaming response
- GET /api/ai/status — check configured provider
- History capped at 20 messages, content at 4K chars
Provider registry expanded to 20 providers:
- 9 device integrations (strava, polar, garmin, whoop, oura, coros, fitbit, withings, suunto)
- 8 AI providers (openai, openrouter, huggingface, groq, ollama, claude, gemini, custom_ai)
- 3 other (usda, nutritionix, telegram)
- Categorized: device, ai_provider, nutrition, notifications
- COROS: MCP bridge approach (no API integration needed)
- Strava: AI/ML warning per their ToS
Frontend ChatCoach.jsx to follow in next commit.
SEC-16: Add frontend ports mapping (80:80) to docker-compose.yml
UI-01: Add routes for Welcome, SettingsIntegrations, MealPlan in App.jsx
SEC-01: Remove traceback leak from auth error responses
SEC-02: Fix require_admin to use is_admin column (was undefined ADMIN_USERNAME)
SEC-03: Add API_TOKEN auth for MCP connector -> backend communication
SEC-04: OAuth state now uses signed JWT tokens (was raw user UUID)
OS-01: First registered user gets admin immediately during registration
10 files changed, 97 insertions(+), 25 deletions(-)
Meal plan now includes:
- Bilingual headers throughout (day names, meal labels, notes)
- Full recipes for all 11 meals inline with bilingual ingredient tables
- Thai translation of every cooking step
- Bilingual prep schedule and substitution rules
Shopping list already had bilingual item tables; added Thai to intro and cost sections.
Two bugs fixed:
1. Backend: list_programs endpoint did not include catalog_id in its
response, so the frontend filter (p.catalog_id) always matched zero
programs. The previous LogActivity card never rendered because
loadActiveProgram() found no active program with a catalog link.
Now also exposes current_week.
2. UX restructure: instead of showing the program workout card at the
top of /log unconditionally, the card now appears AFTER the user
selects the 'Workout' category — matching the natural flow:
Log -> Workout -> see today's program workout option.
The Workout category now reveals:
- Active program banner (clickable, shows program name + week,
navigates to ProgramDetail on tap)
- Today's workout card with exercise preview and one-tap 75 pts
- Other uncompleted workouts in the current week (up to 3)
- 'Or log freeform' divider before the existing form
When all workouts in the current week are done, a green completion
banner appears instead.
Refactored the workout card into a ProgramWorkoutCard component since
it's now used for both today's featured workout and weekly upcoming.
Dashboard:
- The program bar at the top of the home page is now clickable and
navigates to the program detail page. Backend get_today_status now
returns program_id alongside name/day/total_days.
LogActivity:
- On mount, fetches the user's active catalog program and its today's
workout. If there's an uncompleted non-rest workout for today, a
featured 'Today's Program Workout' card appears at the top with an
exercise preview and a one-tap '✓ Complete — 75 pts' button.
- Tapping Complete fires the same /workout/{id}/complete endpoint as
ProgramDetail, surfaces weekly/completion bonuses in the success
message, and returns to the Dashboard.
- The standard category grid below is now relabeled 'Or log something
else' when a program workout is available.
CatalogDetail, ProgramDetail, ProgramSearch and SupportAdmin all use
inline-styled root divs instead of the .page CSS class, so they were
missing the padding-bottom: 96px that clears the fixed bottom nav bar.
Last button/section was hidden behind the nav.
Adds paddingBottom: 96px to each page's root div. Support.jsx has its
own full-height chat layout and is intentionally left as-is.
Catalog stores only the template (typically week 1) but programs span
many weeks. The schedule endpoint was returning only the literal stored
rows, so after day 3 of StrongLifts a user would see no upcoming
workout despite being mid-program.
Changes:
- Add week_number column to workout_logs (migration in main.py) so the
same template workout can be completed once per real week
- Synthetic workout IDs: '{template_uuid}::{real_week}' — frontend
treats opaquely, backend parses on every endpoint
- get_program_schedule rotates the template across catalog.duration_weeks
real weeks and returns synthetic IDs
- today_workout: first uncompleted non-rest entry in current real week
(more flexible than literal day-of-week match)
- complete_workout / get_workout_detail parse synthetic IDs and track
completion per (template_id, real_week) pair
- check_weekly_bonus filters by workout_logs.week_number for the real week
- check_completion_bonus and progress endpoint compute total as
template_per_week * total_weeks, not just template count
The 'View Details' button on ProgramSearch navigated to /catalog/:id
but no route or page existed. Now creates a proper detail view that
fetches via getCatalogProgram, shows program metadata, weeks/workouts,
progression rules, and an Adopt button that redirects to the user's
new program tracking page on success.