From 75d8a8dd0dbd599f20b8e655a024e3775833cb8b Mon Sep 17 00:00:00 2001 From: Christophe Vila Date: Fri, 17 Jul 2026 19:36:51 +0200 Subject: [PATCH] docs: fix stale env-var docs, document validateProfile's zone-range rationale Two Minor findings from the final whole-branch review: the smartrun-dev skill still told developers to set GARMIN_EMAIL/GARMIN_PASSWORD (now sourced from the profile row instead), and validateProfile's deliberate choice not to require 0-100% HR zone coverage had no explanation. --- .claude/skills/smartrun-dev/SKILL.md | 2 +- backend/internal/api/profile.go | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.claude/skills/smartrun-dev/SKILL.md b/.claude/skills/smartrun-dev/SKILL.md index 314a55f..0c15859 100644 --- a/.claude/skills/smartrun-dev/SKILL.md +++ b/.claude/skills/smartrun-dev/SKILL.md @@ -75,7 +75,7 @@ Each `workout_kinds.rule_json` is a recursive AND/OR condition tree (`internal/c ## Dev workflow -- Backend: `cd backend && go run ./cmd/smartrund` (needs `MCP_GARMIN_PYTHON`, `MCP_GARMIN_SERVER`, `GARMIN_EMAIL`, `GARMIN_PASSWORD` env vars; see `internal/config/config.go` for all knobs). +- Backend: `cd backend && go run ./cmd/smartrund` (needs `MCP_GARMIN_PYTHON`/`MCP_GARMIN_SERVER` env vars for the mcp-garmin subprocess paths; see `internal/config/config.go` for all knobs). Garmin credentials are **not** env vars — they live in the `profile` singleton row (`internal/store.Profile`), set via the frontend's Profile page or directly through `PUT /api/profile`. - Frontend: `cd frontend && npm run dev` (set `VITE_API_BASE_URL` if the backend isn't on `localhost:8080`). - No live Garmin account needed for frontend/UI work: `go run ./cmd/seedsample -db /tmp/sample.db` seeds realistic activities/laps/kinds and runs them through the real classification engine, then point `smartrund` at that DB. - `internal/garmin/mock` provides a fake `Client` for tests that need to exercise `internal/sync`/`internal/api` without a live subprocess. diff --git a/backend/internal/api/profile.go b/backend/internal/api/profile.go index a5c61a8..8caee52 100644 --- a/backend/internal/api/profile.go +++ b/backend/internal/api/profile.go @@ -8,6 +8,11 @@ import ( "smartrun/backend/internal/store" ) +// validateProfile checks the HR zones are ascending and gap-free once they +// start, but deliberately does NOT require zone 1 to start at 0% or zone 5 +// to end at 100%: real Karvonen HR training zones (e.g. the seeded defaults, +// 50-60/60-70/70-80/80-90/90-100) don't cover the 0-50% range at all -- +// below zone 1 simply isn't a named training zone. func validateProfile(p store.Profile) error { if p.RestingHeartRate != nil && p.MaxHeartRate != nil && *p.RestingHeartRate >= *p.MaxHeartRate { return errors.New("resting heart rate must be less than max heart rate")