Commit Graph

17 Commits

Author SHA1 Message Date
a12fe3e810 fix(api): stop promoting the ephemeral Garmin client after setup completes
handleSetupComplete reused the onboarding client object as-is in the
permanent per-user cache, but its garmin.Config.TokenStorePath was fixed
at construction to the ephemeral setup/{hash} directory and never
corrected after that directory was renamed to the permanent {userID}
path. The next respawn of that same client (e.g. any Profile-page save,
which unconditionally calls UpdateCredentials) wrote a fresh token file
back under setup/{hash}, forcing a real re-login/MFA on the next Garmin
connect even though a valid session already existed under {userID}.

Close the ephemeral client instead and let the next garminFor call build
a fresh one against the correct, already-renamed directory.
2026-07-26 21:23:24 +02:00
4d2cbe4883 refactor: remove automatic background incremental sync
Garmin's rate-limiting means every unattended sync attempt risks a
ban; syncing should only ever happen when explicitly triggered via
"Sync now" (POST /api/sync/run), never on an unattended timer.

Removes the periodic background loop (main.go's runIncrementalSyncLoop,
api.Server.RunIncrementalSyncForAllUsers), its GENIUSRUN_INCREMENTAL_SYNC_EVERY
config, and store.DB.ListUsers (which existed solely to feed it). The
manual "Sync now" flow (Backfill/IncrementalSync/FillPendingDetails via
FullSync) is untouched.
2026-07-26 18:01:41 +02:00
1d4b1cccfd feat(api): add structured JSON access log with request-id correlation 2026-07-26 14:30:39 +02:00
7d68af5b3c feat(api): defer account creation until Garmin actually connects 2026-07-26 13:30:51 +02:00
ab8cdea214 feat(api): add DELETE /api/profile with Garmin client/tokenstore teardown 2026-07-26 10:19:00 +02:00
1b9088bd03 chore: wire up the new garmin.Config shape, refresh start.sh and CLAUDE.md
Removes the last references to garmin.Config.ServerPath and the
MCP_GARMIN_* env vars now that the wrapper script is embedded in the
binary; updates CLAUDE.md's mcp-garmin section to describe the direct
wrapper instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-25 21:18:50 +02:00
5930cc4ef5 api: build per-user garmin.Client/sync.Service via lazy caching
Server no longer holds one fixed Garmin/Sync pair -- garminFor/syncFor
build and cache one instance per user, keyed off their own profile's
Garmin credentials and a per-user token-store subdirectory. The background
incremental sync loop now iterates every provisioned user each tick
instead of syncing one global account.
2026-07-25 18:10:09 +02:00
2cda0adbf8 api: fix chi middleware-ordering panic in resolveUser registration
r.Use(s.resolveUser) was registered after /session/me and /session/logout
had already been added to the same chi inline-mux group. Chi requires all
r.Use() calls on a mux to precede any route registration on it, or it
panics with "chi: all middlewares must be defined before routes on a mux"
(reproduced against the real chi v5.3.1 dependency). This meant
server.Router() would crash at startup, taking down every internal/api
test that builds a Router along with it.

Separately, since chi captures each route's middleware chain at
registration time, /session/me and /session/logout would never have run
resolveUser even without the panic -- so handleSessionMe's
has_profile/display_name logic could never see a resolved user on that
route.

Fix: move r.Use(s.resolveUser) immediately after
r.Use(auth.RequireSession(...)), before any route in the group is
registered, so the ordering is legal and resolveUser applies to
/session/me, /session/logout, and /setup alike.
2026-07-25 18:02:49 +02:00
4dceb03fc0 api: add user-resolution middleware and POST /api/setup
resolveUser attaches the session's provisioned geniusrun user (if any) to
request context without blocking; requireProvisionedUser (wired fully in
Task 13) 403s routes that need one. GET /api/session/me now reports
has_profile/display_name so the frontend can show the setup screen.
2026-07-25 17:55:13 +02:00
26b903721a api: gate all routes behind an OIDC session, add session endpoints
Router() now wraps every route except /health, /session/login, and
/session/callback in a chi group requiring a valid session cookie
(auth.RequireSession). Adds internal/api/session.go with the four
session HTTP handlers (login/callback/logout/me) and SessionConfig.
NewServer takes an auth.Verifier and SessionConfig. Test infra
(doJSON, newTestServer) now mints/attaches a signed session cookie
automatically so the 36 pre-existing tests keep exercising the
already-logged-in path unchanged, plus 8 new tests cover the gating
and session endpoints themselves.
2026-07-24 21:36:00 +02:00
f9d85e16ef Rename smartrun to geniusrun throughout the codebase
Updates the Go module path, cmd/smartrund -> cmd/geniusrund, the
smartrun-dev skill, .gitignore, and every reference in docs/CLAUDE.md
to match.
2026-07-24 21:08:07 +02:00
518bccf5ab Dedup Garmin data storage, configurable chart colors, taxonomy fixes, sync/progression fixes
- Remove duplicated Garmin fields from storage; decode display-only fields
  (activity name/type, lap duration/HR, structured workout raw JSON) from
  RawJSON at API-response time instead of storing redundant columns
- Add a fully configurable chart color system (pace/HR main-line colors, 4
  effort-kind colors, tint/darken/brighten intensity knobs) under Profile >
  Chart colors
- Rename training types and fix their display order (Easy, Long, 60'/30'
  Threshold, Tempo, Intervals, MAS Test, Race) everywhere they're listed
- Add an Efficiency Factor progression metric; fix Progression chart axes to
  use tight non-zero-based domains, m:ss/km pace formatting, and rounded
  ticks instead of raw floating-point labels
- Expose the raw get_workout_by_id() payload in the raw-data viewer
  alongside activity/lap/detail JSON; enlarge the modal and shrink array
  indentation for readability
- Fix "last sync" reporting a meaningless activity count: record one
  combined sync run per manual "Sync now" and count genuinely new
  activities instead of re-listing whatever Garmin returned for the queried
  window
- Let a Review Queue activity be manually cleared back to Unclassified, and
  make "Reset all" available even while disconnected from Garmin

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-20 06:33:47 +02:00
7f7e4b10f0 Merge backfill into Sync now, replace Full backfill with a destructive Reset all
Sync now now runs Backfill (resumes from the watermark, so a widened history
horizon is picked up automatically) then IncrementalSync then detail-fill, in
one click. "Full backfill" is gone -- in its place, "Reset all" (styled as a
destructive action, gated by a confirm dialog) wipes every synced activity
and its laps/kind assignments and rewinds the backfill watermark, so the next
sync performs a genuinely fresh pull instead of trying to patch up existing
rows with newer schema fields.
2026-07-19 12:41:38 +02:00
0610897541 Replace per-kind reclassify with a single global reclassify that respects manual and Race locks
Manual assignments are the user's definitive word and Race assignments come
from a hard Garmin fact (eventType.typeKey), not a retunable rule -- neither
is ever touched by reclassify again, and Race can no longer be set by hand
via the review queue. Adds an Activities page so this locked/unlocked status
is visible per workout, since Review Queue only ever showed unresolved items.
2026-07-19 11:11:50 +02:00
078c87c413 feat: fix workout taxonomy to 7 types, add pace/HR-zone fields to the API 2026-07-17 19:09:55 +02:00
57be9065cd feat: add profile REST endpoints with HR zone validation
Implements GET /api/profile and PUT /api/profile endpoints with validation
of HR zones (must be contiguous, non-overlapping, 0-100%). Also updates
profile migration to use correct HR zone defaults (0-20, 20-40, etc.)
that match validation requirements.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-17 19:03:29 +02:00
f689f74ae0 Initial commit: smartrun MVP
Garmin run classification and progression tracker. Go backend (MCP
client to mcp-garmin, SQLite store, deterministic rule engine, REST
API) and React/TS frontend (Dashboard, Review Queue, Workout Kinds).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-17 18:33:06 +02:00