Both were only reachable via the periodic background sync loop removed
in 4d2cbe4 -- nothing in production calls them anymore, only tests did.
FullSync already calls backfillCore/incrementalSyncCore directly.
Rewrite the affected tests to call the *Core functions (still exported
within the package) instead, dropping the now-redundant standalone
SyncRun-recording assertion covered by TestFullSync_RecordsOneCombinedSyncRun.
NewService now takes a userID, baked into the instance rather than passed
per-call -- matches internal/api's one-Service-per-logged-in-user model
(Task 13), so ClassifyActivity/Backfill/etc. keep their existing call
signatures unchanged everywhere they're already used.
- 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>
SMARTRUN_BACKFILL_HORIZON_DAYS was a server-startup-only env var with no UI,
defaulting to 3 years -- so editing the unrelated "Rolling window" profile
field (for classification, not sync) had no effect on how far back Sync Now
reached. Backfill horizon is now Profile.BackfillHorizonDays, read fresh on
every Backfill call, with its own field on the Profile page.
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.
Activities recorded from a structured Garmin workout carry a workoutId;
when its steps line up 1:1 with the recorded laps, the per-step target
pace/HR zone is resolved (via a Karvonen lookup for named zones) and stored
on each lap. The Review Queue plots it against the actual per-lap pace/HR
so the user can eyeball whether a run matches its plan while sorting it.
Race is the 8th fixed workout kind, seeded with a real (not placeholder)
rule since Garmin Connect's eventType.typeKey reports "race" for
manually-tagged race activities. Non-running activity types (padel,
cycling, strength training, ...) are now dropped at sync time instead of
being stored. The Review Queue's type filter is now clickable exclusive
pill buttons instead of a dropdown.